toga-ai 1.0.422 → 1.0.424

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.
@@ -21,7 +21,7 @@
21
21
  | [_Model magic-field access (__get without __isset)](features/model-magic-field-access.md) | `_Model` exposes DB columns as "magic" properties via `__get()`, but it defines **no** `__isset()`. | _underscore/Model/Core/Model.php |
22
22
  | [_Model::save() vs raw _Query — no atomic conditional update](features/model-save-vs-query-atomic-update.md) | `_Model::save()` is a plain load-then-write ORM primitive and **cannot express an atomic conditional update** (an optimistic-concurrency / row-claim guard such | _underscore/Model.php, _underscore/Query.php |
23
23
  | [NetSuite REST Client (_Component_Api_Netsuite) — record writes & SuiteQL](features/netsuite-rest-client.md) | `_Component_Api_Netsuite` is the **2.0 `_underscore` NetSuite REST client** — the shared primitive every worker2/api2 NetSuite caller uses for record GETs, Suit | _underscore/Component/Api/Netsuite/Netsuite.php |
24
- | [Per-Client Database Connections & the Local Logs Trap](features/per-client-database-connections.md) | When `_underscore` serves a request for a client it opens **three distinct per-client database connections**, not one. | _underscore/Database.php, _underscore/ApiRequest.php, _underscore/Model/Client/Logs/Api.php |
24
+ | [Per-Client Database Connections & the Local Logs Trap](features/per-client-database-connections.md) | When `_underscore` serves a request for a client it opens **three distinct per-client database connections**, not one. | _underscore/Database.php, _underscore/ApiRequest.php, _underscore/Model/Client/Logs/Api.php, api2/Controller/Index.php |
25
25
  | [Persona Name Translation (PersonaTranslations sidecar)](features/persona-name-translation.md) | Serves Persona **names** in multiple languages by adding a per-language **sidecar** table `PersonaTranslations`, reusing the platform's existing metadata-driven | _underscore/Model/Client/PersonaTranslation.php, dbchanges2/Client/2026-07-22b - PersonaTranslations.sql, dbchanges2/Core/2026-07-22a - PersonaTranslationsRecord.sql, dbchanges2/Client/2026-07-22c - PersonaTranslationsAcl.sql, dbchanges2/Client_CompassCanada/2026-07-22 - PersonaTranslationsFrench.sql, toga2-commerce/src/pages/Account/view/MySettingsView.tsx |
26
26
  | [Recursive Item Fulfillments (upstream mirroring)](features/recursive-item-fulfillments.md) | In a multi-tier supply chain a sales order (SO) spawns a purchase order (PO) that becomes another SO downstream, and so on. | _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Client/ItemFulfillmentItem.php, _underscore/Model/Client/ItemFulfillmentItemUnit.php, _underscore/Model/Client/ItemFulfillmentPackage.php, _underscore/Model/Compass/AdvanceShippingNotice.php, dbchanges2/Core/2026-02-13 - 75601 - RecursiveItemFulfillmentCreation.sql, dbchanges2/Core/2026-06-04 - RecursiveItemFulfillmentPut.sql, dbchanges2/Client_Compass/2026-07-02a - FixSA133377TrackingSerialAndDuplicateIF.sql |
27
27
  | [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, dbchanges2/Client_Compass/2026-07-15f - SalesOrderRecordActionsRemoveDeadConfigRuleOverrides.sql, dbchanges2/Core/2026-07-17h - Update - ClearApprovalsFilterButtonConfig.sql, dbchanges2/Client_Compass/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Compass/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_Quad/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Quad/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.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, api2/Component/Api/V2/V2.php |
@@ -6,7 +6,7 @@ project: _Underscore
6
6
  client: shared
7
7
  type: architecture
8
8
  status: active
9
- updated: 2026-07-16
9
+ updated: 2026-07-23
10
10
  owners: ["jcardinal", "rgirish", "mhammontree"]
11
11
  files:
12
12
  - _underscore/_underscore.php
@@ -344,21 +344,28 @@ multi-file UI components (`.php`/`.html`/`.css`/`.js`) invoked as `<_ComponentNa
344
344
  - **`_Database::register()` auto-starts a lazy transaction (since Apr 2 2026, commit `fa7835ed`).** Any code that calls `register()` and then writes to that DB must call `_Database::transactionCommit()` before the request ends — otherwise MySQL silently rolls back all writes when the connection closes. Lazy transactions only materialise on the first write, so read-only callers are unaffected. See `_underscore/Database.php:48`. First discovered when Rate SAML user provisioning silently discarded all new user INSERTs (Jun 2026).
345
345
  - **PHP "Unclosed '{'" parse errors report a MISLEADING line number.** When a `.php` file loaded by the autoloader (`Loader.php`) has a dropped/unbalanced brace, PHP reports `Unclosed '{' on line N` where N is the **outermost `class X {` line** and fails at EOF — NOT at the true location of the missing `}`. Worse, because the file loads lazily via the SPL autoloader, the runtime trace points at the **caller** that triggered the autoload (e.g. a `new _Email()` call site), not the broken file. **Triage rule:** for an "Unclosed '{'" error, the real culprit is a missing `}` somewhere between the reported line and EOF of the file that failed to load — run `php -l <file>` (it reports the EOF line) and scan the whole file. **Merge-conflict resolutions are a common source of a single dropped brace** — review the entire merge, not just the one file the error appears to name. (First hit: production 500 EO-1, Jul 2026 — a `}` dropped from `_Email::send()` during merge `685e4a14` surfaced as a trace pointing at the `new _Email()` caller.)
346
346
 
347
- - **A controller exception is silently swallowed by `Route.php`, then masked as a view error.** In
348
- dispatch, `_underscore/Route.php` (~lines 462–468) wraps the controller call in
347
+ - **A controller exception used to be silently swallowed by `Route.php`, then masked as a view error
348
+ (FIXED 2026-07-23).** In dispatch, `_underscore/Route.php` previously wrapped the controller call in
349
349
  `catch (Exception $e) { _Database::transactionRollback(); }` — **no log, no rethrow, no debug
350
- bypass.** The controller response is left null, so Route.php falls through to its generic throw at
351
- line 525: `Failed to determine how to render view for route '<route>' ... './View/Index/<method>.html'`.
352
- **Therefore this "Failed to determine how to render view" fatal is very often a MASK for a real
353
- exception thrown inside the controller method** (commonly `_Controller_Index::api()`), not a genuine
354
- routing/view misconfiguration. A frequent culprit is a DB connect / "Unknown database" failure in
355
- `api()`'s **unguarded pre-execute block** (it registers `DB_LOGS` and queries `Logs.Api` for the
356
- transactionId-uniqueness check *before/outside* the only try/catch, which wraps just
357
- `$api->execute()` at ~line 205). When diagnosing the line-525 error, look for a thrown exception in
358
- the controller — not the view layer. (Note: an *unmatched host* does NOT trigger this; `api()`
359
- returns a response object that gets JSON-encoded — only a thrown exception hits the line-525 path.)
360
- See `2.0/apps/api2/workflows/environment-configuration-and-provisioning.md`. Fix candidate: have
361
- Route.php log/rethrow the swallowed exception (or expose it under debug) instead of discarding it.
350
+ bypass.** The controller response was left null, so Route.php fell through to its generic throw:
351
+ `Failed to determine how to render view for route '<route>' ... './View/Index/<method>.html'`.
352
+ That "Failed to determine how to render view" fatal was therefore very often a MASK for a real
353
+ exception thrown inside the controller method (commonly `_Controller_Index::api()`), not a genuine
354
+ routing/view misconfiguration. **Fixed 2026-07-23:** the catch now logs the swallowed exception
355
+ (via `error_log`, message + trace) **and rethrows**, and it catches `\Throwable` (not just
356
+ `Exception`) so `Error`s (type errors, fatals) surface too — the rollback is still performed before
357
+ rethrow. The real exception now propagates instead of being reshaped into the misleading line-525
358
+ view error. When diagnosing, you now see the true controller exception directly. (Note: an
359
+ *unmatched host* does NOT trigger this; `api()` returns a response object that gets JSON-encoded —
360
+ only a thrown exception hits this path.) A frequent underlying culprit was a DB connect / "Unknown
361
+ database" failure in `api()`'s pre-execute block — now separately guarded in api2 (see the api2
362
+ architecture doc's front-controller section). See
363
+ `2.0/apps/api2/workflows/environment-configuration-and-provisioning.md`.
364
+ - **Follow-up (deferred, separate ticket): production info-leak from unconditional `display_errors`.**
365
+ Now that Route.php rethrows, whatever global error handling renders the exception can leak the raw
366
+ message/trace to the client if `display_errors` is on unconditionally. Confirm `display_errors` is
367
+ disabled in production and that the framework's error surface returns a sanitized envelope (not a raw
368
+ stack trace) to API consumers. Not yet done — tracked as a follow-up.
362
369
 
363
370
  ## Change history
364
371
  - 2026-06-11 — Documented lazy transaction gotcha in `_Database::register()` (rgirish)
@@ -367,3 +374,4 @@ multi-file UI components (`.php`/`.html`/`.css`/`.js`) invoked as `<_ComponentNa
367
374
  - 2026-07-02 — Fixed framework-wide `FIELD_STORAGE` write-drop and folder-read bugs in `Model.php`; noted the remaining unconditional-refetch dirty-read follow-up. (mhammontree)
368
375
  - 2026-07-07 — Documented `_Query` writes-only async mode (`isAsync` → Worker `Infrastructure/Database/Query`) in the database-architecture section, linked `features/async-query-execution.md`, and noted the SQS-first `_Worker::runTask()` enqueue departure (caller does no MySQL; worker tier creates the row on pickup). (jcardinal)
369
376
  - 2026-07-16 — Documented the misleading "Unclosed '{'" parse-error gotcha (autoloader reports the outermost `class {` line / caller trace, not the true dropped-brace location; merge conflicts a common cause). First hit production 500 EO-1 via a dropped `_Email::send()` brace. (jcardinal)
377
+ - 2026-07-23 — Fixed the Route.php swallow→mask gotcha: the controller-call catch now log+rethrows and catches `\Throwable` (not just `Exception`), so the true controller exception surfaces instead of the misleading line-525 "Failed to determine how to render view" fatal. Noted the deferred production `display_errors` info-leak follow-up. (jcardinal)
@@ -6,12 +6,13 @@ project: _Underscore
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-07-09
9
+ updated: 2026-07-23
10
10
  owners: ["dfranks", "jcardinal", "mhammontree", "apeterson"]
11
11
  files:
12
12
  - _underscore/Database.php
13
13
  - _underscore/ApiRequest.php
14
14
  - _underscore/Model/Client/Logs/Api.php
15
+ - api2/Controller/Index.php
15
16
  related:
16
17
  - ../architecture.md
17
18
  - ../workflows/local-db-refresh-from-beta.md
@@ -89,12 +90,29 @@ registration is region-aware in `api2/Controller/Index.php`; the alias const is
89
90
  set from beta — see [Refreshing a local dev DB from beta](../workflows/local-db-refresh-from-beta.md).
90
91
  This is a **local-environment** gap, not a code bug — do not "fix" it by disabling logging in
91
92
  shared code.
93
+ - **The shared Core Logs DB (`DB_LOGS`) has the same trap — and its name is resolved dynamically,
94
+ so "I have all the databases installed" can still fail.** api2's `Controller/Index.php::api()`
95
+ bootstrap registers `DB_LOGS` using a schema name read from a **`Core.Database` row**
96
+ (`id = CORE_LOGS_DATABASE_ID`), not a fixed literal. A Logs DB you created locally whose *name*
97
+ doesn't match that Core row still reads as **missing** → `mysqli_connect` `Unknown database`. Match
98
+ the local schema name to the `Core.Database` row (or fix the row), don't just "create a logs DB."
99
+ - **This Core Logs bootstrap runs OUTSIDE the try/catch that wraps `$api->execute()`** (the env
100
+ lookup, region/host `search()`, `_Model_Core_Database` instantiation, and `_Database::register()`
101
+ for `DB_CORE`/`DB_LOGS` all run before the guarded block at ~line 202 of `Controller/Index.php`).
102
+ A connect failure here threw past `api()` and got masked by `Route.php` as the misleading line-525
103
+ "Failed to determine how to render view" error — see the Route.php swallow→mask gotcha in
104
+ [_underscore architecture](../architecture.md#gotchas--known-issues).
92
105
  - Related 1.0 analogue: the legacy `App_` worker has the same hazard writing to `Logs.API`
93
106
  (`db_logs`) — the laptop trap there is documented separately in the worker NetSuite bootstrap
94
107
  notes.
95
108
 
96
109
  ## Change history
97
110
 
111
+ - 2026-07-23 — Documented the shared **Core Logs** (`DB_LOGS`) analogue of the logs trap: its schema
112
+ name is resolved from a `Core.Database` row (`id = CORE_LOGS_DATABASE_ID`) in
113
+ `api2/Controller/Index.php::api()`, so a name-mismatched local Logs DB still reads as missing; and
114
+ this bootstrap runs outside `api()`'s guarded block, so a connect failure was masked by Route.php's
115
+ line-525 view error. (jcardinal)
98
116
  - 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)
99
117
  - 2026-07-09 — Linked the new [local-DB-refresh-from-beta workflow](../workflows/local-db-refresh-from-beta.md)
100
118
  from the logs-trap fix (refreshing all local schemas from beta covers the missing `Logs_<Id>`). (apeterson)
@@ -6,7 +6,7 @@ project: API
6
6
  client: shared
7
7
  type: architecture
8
8
  status: active
9
- updated: 2026-07-07
9
+ updated: 2026-07-23
10
10
  owners: [jcardinal, bala]
11
11
  files:
12
12
  - api2/Controller/Index.php
@@ -65,8 +65,21 @@ lands in `api()`.
65
65
  - **CORS:** permissive `Access-Control-Allow-*`; returns 200 for `OPTIONS` preflight.
66
66
  - **`/health`:** 200 + empty object (EB/LB probe — keep it working).
67
67
  - **Sentry:** initialized per request; EC2 instance metadata attached.
68
- - **Core DB:** registers `Core` (`DB_CORE`), preferring a region-local read host
69
- (`CORE_READHOST`). **Core Logs DB** (`DatabaseHost` id 11) registered region-aware as `DB_LOGS`.
68
+ - **Core DB bootstrap (now guarded, 2026-07-23):** registers `Core` (`DB_CORE`), preferring a
69
+ region-local read host (`CORE_READHOST`), then registers the **Core Logs DB** region-aware as
70
+ `DB_LOGS`. This whole block runs in a **pre-execute phase that executes *before* the `execute()`
71
+ try/catch**, so a failure here historically escaped that guard and became a fatal. Two traps:
72
+ (1) the Core Logs **schema name is resolved from a `Core.Database` row** (`id =
73
+ CORE_LOGS_DATABASE_ID`), so a **name-mismatched local Logs DB** (one whose actual schema name
74
+ differs from the Core-recorded name) reads as **missing** and `mysqli_connect` throws `Unknown
75
+ database` — a common local-dev breakage, not a code bug. (2) That connect failure used to be a
76
+ fatal. **Fixed 2026-07-23:** the pre-execute Core/Logs bootstrap is wrapped in a
77
+ `try/catch (\Throwable)` that, on failure, returns the standard **`INVALID_CONFIGURATION`**
78
+ error envelope and reports to **Sentry** instead of dying with an unguarded fatal. Note the
79
+ rollback in that catch is a **guarded no-op** — with no transaction started yet,
80
+ `_Database::transactionRollback()` short-circuits safely (`Database.php:219–226`). See
81
+ [_underscore architecture — Route.php log+rethrow gotcha](../_underscore/architecture.md) for the
82
+ upstream masking behavior this pairs with, and the per-client-database-connections feature doc.
70
83
  - **Host dispatch:** lowercase `HTTP_HOST`, strip trailing domain, switch on the remainder.
71
84
  - **Transaction/logging invariant (JSON path):** wraps the call in transactions; on
72
85
  **success** commit logs then data; on **failure** commit logs but **roll back data**; on
@@ -153,6 +166,12 @@ Core/Client/Logs DB passwords, **AWS access key id + secret**, and third-party A
153
166
  carries a **GitHub PAT**. These should be rotated and moved to SSM Parameter Store / EB env
154
167
  properties. **Flag this if you touch config or deploy.**
155
168
 
169
+ **Follow-up (deferred, separate ticket): raw exception disclosure to clients.** The error paths that
170
+ surface a caught `\Throwable` (now that Route.php rethrows and the bootstrap guard reports failures)
171
+ must not return raw `getMessage()`/`getTrace()` output in the client-facing envelope — that leaks
172
+ internal paths, schema names, and stack frames to API consumers. Sanitize the client envelope
173
+ (generic message + code; full detail to Sentry/logs only). Not yet done.
174
+
156
175
  ## When making changes here
157
176
 
158
177
  - **Adding/altering an endpoint is usually a data change, not code** — define `Records` +
@@ -167,3 +186,6 @@ properties. **Flag this if you touch config or deploy.**
167
186
  the duplicate check.
168
187
  - Preserve the commit-logs / rollback-data-on-failure invariant when editing the controller
169
188
  or `execute()`.
189
+
190
+ ## Change history
191
+ - 2026-07-23 — Documented the now-guarded Core/Logs DB bootstrap in the front controller: the pre-execute block runs before the `execute()` try/catch, the Core Logs schema name is resolved from a `Core.Database` row (`id = CORE_LOGS_DATABASE_ID`) so a name-mismatched local Logs DB reads as missing, and the failure is now wrapped in `try/catch (\Throwable)` returning `INVALID_CONFIGURATION` + Sentry instead of a fatal (guarded no-op rollback, `Database.php:219–226`). Added the deferred raw-getMessage/getTrace client-disclosure follow-up to the Security note. (jcardinal)
@@ -6,8 +6,8 @@ project: TOGa Blox
6
6
  client: shared
7
7
  type: workflow
8
8
  status: active
9
- updated: 2026-07-21
10
- owners: [jcardinal]
9
+ updated: 2026-07-23
10
+ owners: [jcardinal, apeterson]
11
11
  files:
12
12
  - toga-blox/.github/workflows/publish.yml
13
13
  - toga-blox/src/utils/getFontAwesomeIcon.tsx
@@ -35,6 +35,26 @@ resolve in a consuming app's build, or touching `publish.yml`.
35
35
  - **Every** branch (including `_production`) publishes a **prerelease** version
36
36
  `X.Y.Z-<mode>.<run>` under dist-tag `<mode>`. There is no longer a production special case.
37
37
  Verified live: `production:1.0.315-production.96`, `sandbox-client:1.0.315-sandbox-client.97`.
38
+ Observed 2026-07-23: `sandbox-client:1.0.317-sandbox-client.101`, `sandbox-dev:1.0.316-sandbox-dev.102`.
39
+
40
+ ### Version derivation (CI owns the suffix; dev owns only the base)
41
+
42
+ - CI reads `version` from `package.json`, **strips any `-<suffix>`**, does **`patch + 1`**, then
43
+ appends `-<mode>.<GITHUB_RUN_NUMBER>`. So base `1.0.317` on `_sandbox-client` run 104 publishes
44
+ `1.0.318-sandbox-client.104` under dist-tag `sandbox-client`.
45
+ - **The developer controls only the BASE version** committed in `package.json` on the feature
46
+ branch. **Never hand-write the `-<mode>.N` prerelease suffix** — CI generates it. There is no
47
+ manual `npm publish`; CI is the only publisher.
48
+
49
+ ### Deploy procedure (e.g. `_sandbox-client`)
50
+
51
+ 1. Ensure a clean tree; optionally bump the base version in `package.json` on the feature branch.
52
+ 2. Merge the feature branch into `_sandbox-client`, then push (this triggers the CI publish).
53
+ 3. Monitor with `gh run watch`; verify with `npm dist-tag ls @agilant/toga-blox`.
54
+ 4. Consuming apps (e.g. `toga25-supply` on `_sandbox-client`) install from the `sandbox-client`
55
+ dist-tag or a pinned `1.0.x-sandbox-client.N` version and must **re-install** to pick up a new
56
+ build. The `deploy-sandbox-client` project skill (`.claude/skills/` in this repo) drives this
57
+ procedure end-to-end.
38
58
 
39
59
  ### What was removed (and why it's safe)
40
60
 
@@ -66,6 +86,12 @@ environment needs its own maintained `_<mode>` branch in `toga-blox-npm`.
66
86
  - **Publish before build.** `npm install @agilant/toga-blox@<mode>` fails `ETARGET No matching
67
87
  version` in the app build until this repo has published that channel. GitHub Actions runs the
68
88
  workflow version *on the pushed branch*, so the branch must carry the current `publish.yml`.
89
+ - **`publish.yml` has diverged across branches (2026-07-23).** The dynamic `_*` wildcard trigger
90
+ lives on `_sandbox-client`, but older/feature branches (e.g. `feature-new-table`) still carry the
91
+ *old* `publish.yml` that triggers only on `_production/_gamma/_beta`. Because Actions runs the
92
+ copy on the pushed branch, a new `_<mode>` channel will **not** publish unless the branch you push
93
+ carries the wildcard-trigger version. When standing up a new channel, merge from a branch that has
94
+ the dynamic `publish.yml` (or update it first).
69
95
  - **FontAwesome v7 `style`-prop TS2322** (fixed 2026-07-21 in `getFontAwesomeIcon.tsx`, and a
70
96
  reusable pattern for any consumer). FA v7 types the `FontAwesomeIcon` `style` prop as
71
97
  `CSSProperties & CSSVariables`, where `CSSVariables` requires a `--fa-*` custom-property index
@@ -78,6 +104,11 @@ environment needs its own maintained `_<mode>` branch in `toga-blox-npm`.
78
104
  the pattern the consuming apps should follow (see the commerce workflow's secret-hygiene note).
79
105
 
80
106
  ## Change history
107
+ - 2026-07-23 — Documented CI version derivation (strip suffix → patch+1 → append `-<mode>.<run>`;
108
+ dev owns only the base version, never the suffix) and the `_sandbox-client` deploy procedure now
109
+ driven by the `deploy-sandbox-client` project skill. Recorded that `publish.yml` has diverged
110
+ across branches: the dynamic `_*` trigger is on `_sandbox-client` while older/feature branches
111
+ still trigger only on `_production/_gamma/_beta`. (apeterson)
81
112
  - 2026-07-21 — Made `publish.yml` fully dynamic: trigger `_*`, branch→dist-tag by stripping the
82
113
  leading underscore (mode == channel 1:1), every branch (incl. `_production`) publishes a
83
114
  `X.Y.Z-<mode>.<run>` prerelease; removed the `_production`→`latest` special case (workflow no
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.422",
3
+ "version": "1.0.424",
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",