toga-ai 1.0.522 → 1.0.524
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.
- package/knowledge/1.0/apps/library/features/error-capture-1-0.md +45 -5
- package/knowledge/1.0/apps/tools/features/errors-curation-console.md +34 -4
- package/knowledge/2.0/apps/_underscore/features/error-reporting-issue-event.md +126 -11
- package/knowledge/2.0/apps/toga-blox/INDEX.md +1 -1
- package/knowledge/2.0/apps/toga-blox/workflows/dynamic-publish-pipeline.md +27 -1
- package/knowledge/2.0/apps/worker2/features/error-escalation-cron.md +8 -0
- package/package.json +1 -1
|
@@ -134,11 +134,37 @@ written for a reader and carries no file, line, or trace.
|
|
|
134
134
|
### `Logs.Event.context` shape
|
|
135
135
|
|
|
136
136
|
Identical in both frameworks: **every top-level key holds an object**, and scalars are grouped
|
|
137
|
-
under a `
|
|
138
|
-
`
|
|
139
|
-
not `[]`. `$_POST` is captured and redacted on the same
|
|
140
|
-
populates `$_POST` for form encodings, so JSON API bodies
|
|
141
|
-
over-length shed list was renamed in lockstep and its marker is an
|
|
137
|
+
under a `REFERENCE` object. Top-level keys are **UPPERCASE** display names (changed 2026-08-04) —
|
|
138
|
+
`REFERENCE`, `GET`, `POST`, `RUNTIME`, `AWS`, `SERVER`, `GLOBALS`. `GET`/`POST` are cast to objects
|
|
139
|
+
so an empty one serializes as `{}`, not `[]`. `$_POST` is captured and redacted on the same
|
|
140
|
+
key-name basis as `$_GET` (note PHP only populates `$_POST` for form encodings, so JSON API bodies
|
|
141
|
+
deliberately do not appear). The over-length shed list was renamed in lockstep and its marker is an
|
|
142
|
+
object too. Four shapes of this payload coexist for the 7-day retention window — readers keep the
|
|
143
|
+
`REFERENCE` → `Reference` → `reference` fallback chain.
|
|
144
|
+
|
|
145
|
+
### `$GLOBALS` IS captured (reversal, 2026-08-04) — and 1.0 benefits most
|
|
146
|
+
|
|
147
|
+
`capture.php` mirrors 2.0's `buildGlobalsContext()` / `sanitizeForContext()` /
|
|
148
|
+
`CONTEXT_GLOBALS_TO_EXCLUDE` / `CONTEXT_GLOBALS_MAX_DEPTH` / `CONTEXT_GLOBALS_MAX_STRING_LENGTH`
|
|
149
|
+
exactly. Full rationale, guards, the hostile-fixture results and the **open PII security note**
|
|
150
|
+
live in the
|
|
151
|
+
[2.0 doc](../../../2.0/apps/_underscore/features/error-reporting-issue-event.md#globals-capture--the-earlier-refusal-is-reversed-2026-08-04)
|
|
152
|
+
— read it before touching either copy.
|
|
153
|
+
|
|
154
|
+
Two things are specifically 1.0:
|
|
155
|
+
|
|
156
|
+
- **1.0 crons are the big winner.** They keep almost all working state in globals, so a 1.0 cron's
|
|
157
|
+
captured context is now genuinely rich, where a 2.0 request (state in objects and locals) stays
|
|
158
|
+
thin. The limitation is that `$GLOBALS` reaches **global scope only** — PHP exposes no API for
|
|
159
|
+
another frame's locals; the trace's arguments are the nearest substitute.
|
|
160
|
+
- **`App_Error::redactSensitiveKeys()` is NOT the sanitizer used here.** It is still live on the
|
|
161
|
+
**1.0 Sentry path**, and it redacts only the **top level** of an array and never sees inside an
|
|
162
|
+
object. That weakness is precisely why globals capture was refused before. Do not reach for it
|
|
163
|
+
from capture code, and do not assume it protects anything nested.
|
|
164
|
+
|
|
165
|
+
**PHP 7.2 constraints on the mirror:** no `mixed` type, no named arguments, and no
|
|
166
|
+
`ReflectionProperty::isInitialized()` (7.4+) — `getValue()` is wrapped in its own `try`/`catch`
|
|
167
|
+
instead.
|
|
142
168
|
|
|
143
169
|
### Elastic Beanstalk environment name on AL1
|
|
144
170
|
|
|
@@ -182,6 +208,20 @@ exists in `api2`/`worker2`. So 1.0 reads the AL1 container config at
|
|
|
182
208
|
|
|
183
209
|
## Change history
|
|
184
210
|
|
|
211
|
+
- 2026-08-04 (latest, **uncommitted/undeployed** at time of writing) — **Mirrored 2.0's reversal:
|
|
212
|
+
`$GLOBALS` is now captured** into a top-level `GLOBALS` group in `capture.php`
|
|
213
|
+
(`buildGlobalsContext()`, `sanitizeForContext()`, `CONTEXT_GLOBALS_TO_EXCLUDE`,
|
|
214
|
+
`CONTEXT_GLOBALS_MAX_DEPTH`, `CONTEXT_GLOBALS_MAX_STRING_LENGTH`), byte-compatible with
|
|
215
|
+
`_underscore/Error.php`. 1.0 crons benefit most because they keep working state in globals — which
|
|
216
|
+
is why Sentry's Globals section was so much richer than ours. Recorded that
|
|
217
|
+
`App_Error::redactSensitiveKeys()` (still live on the 1.0 Sentry path) redacts only the top level
|
|
218
|
+
of an array and never enters an object, which is exactly why capture was refused before the
|
|
219
|
+
Reflection-based sanitizer existed. Added `dsn`/`connection`/`passphrase`/`salt` to
|
|
220
|
+
`CONTEXT_SENSITIVE_KEY_FRAGMENTS` (a DSN embeds `user:password`). Top-level context keys are now
|
|
221
|
+
**UPPERCASE**. PHP 7.2 sweep on this copy: no `mixed`, no named arguments, no
|
|
222
|
+
`ReflectionProperty::isInitialized()` — `getValue()` is wrapped in its own try/catch. The **open
|
|
223
|
+
PII exposure** this widens is documented on the 2.0 doc; `library` and `tools` must both deploy
|
|
224
|
+
before any of it is observable. (jcardinal)
|
|
185
225
|
- 2026-08-04 (later, **uncommitted/undeployed** at time of writing) — **Fixed: 1.0 errors recorded
|
|
186
226
|
`clientId` NULL on every cron but one** (2,093 NULL `Logs.Event` rows vs 107 with a client; issue
|
|
187
227
|
`1Q` from `crons/toga2/quad/import_po_and_asn.php`). `App_Api_Toga2::authenticate()` — 1.0's real
|
|
@@ -107,6 +107,17 @@ statement about *response time*, not about the error, and nothing on the page le
|
|
|
107
107
|
those apart. **Nothing renders while the clock is running but under 24h** — below that the clock
|
|
108
108
|
contributes nothing to urgency, so saying "neglect" would overstate it.
|
|
109
109
|
|
|
110
|
+
**The note must render on ONE line** (fixed 2026-08-04). `.neglect-note` was
|
|
111
|
+
`display: inline-block`, so "neglect NORMAL · 26h" broke after "neglect" and the level dropped to a
|
|
112
|
+
second line — the two halves then read as unrelated fragments. It is now `display: block` +
|
|
113
|
+
`white-space: nowrap`.
|
|
114
|
+
|
|
115
|
+
> **⚠ Widening the column is PART of that fix, not a nicety.** Under `table-layout: fixed`, a
|
|
116
|
+
> `nowrap` element in a column too narrow to hold it **overflows the cell** rather than shrinking —
|
|
117
|
+
> so `nowrap` alone would have traded a wrap for an overflow. `col-urgency` went **96px → 136px**
|
|
118
|
+
> and `.issue-listing` `min-width` **1026px → 1066px** to match. Any future `nowrap` in this table
|
|
119
|
+
> needs the same paired column-width change.
|
|
120
|
+
|
|
110
121
|
**Deliberately scoped to the neglect contribution only.** The console never claims *which* axis is
|
|
111
122
|
winning: computing the full driving axis in Tools would mean copying the volume, sustained and
|
|
112
123
|
release-band thresholds too, and that much duplicated policy would rot.
|
|
@@ -209,15 +220,34 @@ They were renamed from plural on 2026-08-01, after the pipeline was already in p
|
|
|
209
220
|
- **The distinct-EB-environment query is a full scan of `Event`.** Acceptable only because that
|
|
210
221
|
table is bounded by the 7-day purge. At volume the proper fix is denormalising the EB environment
|
|
211
222
|
onto its own `Event` column **at capture time**.
|
|
212
|
-
-
|
|
213
|
-
on 2026-08-04 (one object per top-level key, scalars
|
|
214
|
-
|
|
223
|
+
- **⚠ FOUR shapes of `Event.context` coexist — every reader must tolerate all of them.** The shape
|
|
224
|
+
was restructured on 2026-08-04 (one object per top-level key, scalars grouped) and the top-level
|
|
225
|
+
keys were then **uppercased** (`Reference` → `REFERENCE`, `Runtime` → `RUNTIME`). With 7-day
|
|
226
|
+
retention, older shapes stay in the table for a week after each change. `issue/get.php` therefore
|
|
227
|
+
reads **`'REFERENCE'` first, then `'Reference'`, then `'reference'`**, and the JSON-tree
|
|
228
|
+
open-by-default test is **case-insensitive** (`strtolower($key) === 'reference'`) so the reference
|
|
229
|
+
card stays expanded for both casings. The key humanizer needed no change — it already passes
|
|
230
|
+
all-caps keys through untouched. **This fallback-chain pattern is the rule for any context key
|
|
231
|
+
rename, not a one-off.** A new `GLOBALS` group also now appears on newer events (see the
|
|
232
|
+
[2.0 doc](../../../2.0/apps/_underscore/features/error-reporting-issue-event.md)) — it is the
|
|
233
|
+
largest group by far, so the largest-first card ordering will usually put it on top.
|
|
215
234
|
- **Curated text is authoritative.** Once `isManaged` is set, later occurrences no longer update
|
|
216
235
|
subject/description, so a stale curated subject stays stale until someone edits it again.
|
|
217
236
|
|
|
218
237
|
## Change history
|
|
219
238
|
|
|
220
|
-
- 2026-08-04 (latest, **uncommitted/undeployed** at time of writing) — **
|
|
239
|
+
- 2026-08-04 (latest, **uncommitted/undeployed** at time of writing) — **Fixed:** the neglect
|
|
240
|
+
indicator wrapped onto two lines in the listing — `.neglect-note` was `display: inline-block`, so
|
|
241
|
+
"neglect NORMAL · 26h" broke after "neglect" and read as two unrelated fragments. Now
|
|
242
|
+
`display: block` + `white-space: nowrap`, **with `col-urgency` 96px → 136px and `.issue-listing`
|
|
243
|
+
min-width 1026px → 1066px as part of the same fix**: under `table-layout: fixed` a `nowrap`
|
|
244
|
+
element in a too-narrow column overflows the cell instead of shrinking, so nowrap alone would have
|
|
245
|
+
traded a wrap for an overflow. **Changed:** capture now emits **UPPERCASE** top-level
|
|
246
|
+
`Event.context` keys, so `issue/get.php` reads `'REFERENCE'` → `'Reference'` → `'reference'` and
|
|
247
|
+
its JSON-tree open-by-default test became case-insensitive; **four shapes of the payload now
|
|
248
|
+
coexist** across the 7-day retention window and every reader must tolerate them. A new `GLOBALS`
|
|
249
|
+
context group also starts appearing on newer events. (jcardinal)
|
|
250
|
+
- 2026-08-04 (earlier) — **Built:** a **neglect
|
|
221
251
|
indicator** on both pages — listing gained an `openTaskAgeHours` correlated subquery and a
|
|
222
252
|
`$describeNeglect` amber note (`.neglect-note`, `#8a5a00`) under the urgency badge; the detail
|
|
223
253
|
page's *Acknowledged* row now reads "not yet — waiting 3 days" with a NORMAL/HIGH neglect chip.
|
|
@@ -177,18 +177,98 @@ read the ambient value.
|
|
|
177
177
|
|
|
178
178
|
### `Event.context` shape (uniform, both frameworks)
|
|
179
179
|
|
|
180
|
-
**Every top-level key holds an object**; scalars are grouped under a `
|
|
181
|
-
|
|
180
|
+
**Every top-level key holds an object**; scalars are grouped under a `REFERENCE` object.
|
|
181
|
+
**Top-level keys are UPPERCASE** (changed 2026-08-04): `REFERENCE`, `GET`, `POST`, `RUNTIME`,
|
|
182
|
+
`AWS`, `SERVER`, `GLOBALS`. `GET`/`POST`/`AWS`/`SERVER` already were; `Reference` → `REFERENCE`
|
|
183
|
+
and `Runtime` → `RUNTIME` bring the rest into line. These are **group headings, not data**, and
|
|
184
|
+
the mixed casing read as though the difference meant something.
|
|
185
|
+
|
|
182
186
|
`GET`/`POST` are cast to objects so an empty one serializes as `{}` rather than `[]`. `$_POST` is
|
|
183
|
-
|
|
187
|
+
captured (redacted on the same key-name basis as `$_GET`; PHP only populates `$_POST` for form
|
|
184
188
|
encodings, so JSON API bodies deliberately do not appear). The duplicate top-level
|
|
185
189
|
`peakMemoryMb` was dropped, the over-length shed list was renamed in lockstep with its marker now
|
|
186
190
|
an object, and 2.0 gained `framework` / `isBusiness` / `issueKey` / `errorFile` / `errorLine` for
|
|
187
191
|
1.0 parity.
|
|
188
192
|
|
|
189
193
|
A uniform shape is what lets a viewer render context **generically** (see the Tools console's
|
|
190
|
-
card/JSON-tree renderer).
|
|
191
|
-
|
|
194
|
+
card/JSON-tree renderer).
|
|
195
|
+
|
|
196
|
+
> **⚠ FOUR shapes of this payload now coexist in `Event`.** Retention is 7 days, so every reader
|
|
197
|
+
> must tolerate the older ones until they purge. Tools reads `'REFERENCE'` first and falls back to
|
|
198
|
+
> `'Reference'` then `'reference'`; its JSON-tree open-by-default test is **case-insensitive**
|
|
199
|
+
> (`strtolower($key) === 'reference'`) so the reference card stays expanded for every shape. The
|
|
200
|
+
> Tools key humanizer already passes all-caps keys through untouched. **Never rename a context key
|
|
201
|
+
> without leaving the fallback chain in place.**
|
|
202
|
+
|
|
203
|
+
### `GLOBALS` capture — the earlier refusal is REVERSED (2026-08-04)
|
|
204
|
+
|
|
205
|
+
**`$GLOBALS` IS now captured**, into a new top-level `GLOBALS` group, in **both** frameworks
|
|
206
|
+
(`_underscore/Error.php` `buildGlobalsContext()`; mirrored in `library/app/error/capture.php`).
|
|
207
|
+
|
|
208
|
+
> This **reverses** the earlier decision on this page that `$GLOBALS` would deliberately never be
|
|
209
|
+
> captured. That refusal was correct *for the sanitizer that existed at the time* — a key-name
|
|
210
|
+
> blocklist that recursed **arrays only** and walked straight past framework singletons holding DB
|
|
211
|
+
> credentials in **object properties**. That rationale is now the reason **the sanitizer had to be
|
|
212
|
+
> rewritten**, not a reason to refuse capture.
|
|
213
|
+
|
|
214
|
+
**Why reversed:** our context was compared against Sentry's "Globals" section for the same error
|
|
215
|
+
and carried none of it. The variable that explains a failure is routinely the one nobody thought
|
|
216
|
+
to allowlist in advance, and 1.0 crons in particular keep almost all working state in globals —
|
|
217
|
+
which is exactly why the Sentry payload was so much richer.
|
|
218
|
+
|
|
219
|
+
**What made it safe enough:** `sanitizeForContext()` **recurses into objects via Reflection,
|
|
220
|
+
including PRIVATE and PROTECTED properties**, and tests every key at every depth.
|
|
221
|
+
**Reflection, not `get_object_vars()`** — a credential is far likelier to be private than public,
|
|
222
|
+
and *a sanitizer that cannot see a value cannot redact it*. (1.0's older
|
|
223
|
+
`App_Error::redactSensitiveKeys()`, still live on the 1.0 Sentry path, redacts only the **top
|
|
224
|
+
level** of an array and never looks inside an object. That is the old behaviour, not this one.)
|
|
225
|
+
|
|
226
|
+
Guards:
|
|
227
|
+
|
|
228
|
+
- `CONTEXT_GLOBALS_MAX_DEPTH = 6` — an object graph with parent pointers must not walk a whole
|
|
229
|
+
schema into a log row.
|
|
230
|
+
- `CONTEXT_GLOBALS_MAX_STRING_LENGTH = 2000` per string, with the **true byte length appended**.
|
|
231
|
+
- `CONTEXT_GLOBALS_TO_EXCLUDE` — the superglobals (already their own groups) plus
|
|
232
|
+
`GLOBALS`, `appRoot`, `newAppRoot`, `argv`, `argc`.
|
|
233
|
+
- `Closure`s and resources render as placeholders.
|
|
234
|
+
- **`GLOBALS` is shed FIRST by `encodeContext()`** when the payload exceeds
|
|
235
|
+
`CONTEXT_MAX_LENGTH` (60000), so an oversized dump costs itself and never the curated groups.
|
|
236
|
+
|
|
237
|
+
Verified against a deliberately hostile fixture: a private `$dbPassword` nested inside an object
|
|
238
|
+
property → `[REDACTED]`; `apiSecret` three levels deep in an array → `[REDACTED]`;
|
|
239
|
+
`shippingAddress1`/`shippingPhone`/`shippingEmail` → `[REDACTED]`; a 5000-char string truncated; a
|
|
240
|
+
resource and a `Closure` rendered safely.
|
|
241
|
+
|
|
242
|
+
> **Known limitation — GLOBAL SCOPE ONLY.** A variable local to the function that threw is *not*
|
|
243
|
+
> reachable from a handler; PHP exposes no API for another frame's locals. The stack trace's
|
|
244
|
+
> arguments are the nearest substitute, and the trace already carries them. Practical effect: rich
|
|
245
|
+
> capture for **1.0 crons**, comparatively thin for a **2.0 request** where state lives in objects
|
|
246
|
+
> and locals.
|
|
247
|
+
|
|
248
|
+
#### ⚠ SECURITY — residual PII that key-name redaction cannot catch (open, not solved)
|
|
249
|
+
|
|
250
|
+
`Event.context` is a table **every client shares**, and its contents also flow into **ClickUp task
|
|
251
|
+
bodies and escalation emails**. Key-name redaction cannot recognise PII **by value**, so from a
|
|
252
|
+
real Compass MITS payload these are still stored in clear: `shippingName`, `shippingAttention`
|
|
253
|
+
(person and company names), `shippingCity` / `shippingStateCode` / `shippingZip` (the rest of an
|
|
254
|
+
address whose *line 1* is redacted), and `mitsClientId` (an `odp_pk_`-style publishable-looking
|
|
255
|
+
key that no blocklist flags).
|
|
256
|
+
|
|
257
|
+
This is the **same exposure that was deliberately closed earlier the same day** by dropping the
|
|
258
|
+
`print_r($payload)` dump from
|
|
259
|
+
`worker/crons/toga2/compass/workflow/1_transmit_compass_sales_orders_to_mits.php` — reopened
|
|
260
|
+
through a wider door, knowingly, at the developer's direction. Sentry was an acceptable home for
|
|
261
|
+
this data because it is access-controlled, single-tenant and not mirrored into tickets;
|
|
262
|
+
`Logs.Event` is **none of those**.
|
|
263
|
+
|
|
264
|
+
Three mitigations were offered and **not yet applied** (follow-ups):
|
|
265
|
+
|
|
266
|
+
1. Extend the sensitive-fragment list (`city`, `zip`, `postal`, `attention`, `recipient`).
|
|
267
|
+
2. Add **value-pattern** redaction — things that look like emails, phone numbers and card numbers
|
|
268
|
+
regardless of key name.
|
|
269
|
+
3. Exclude named globals per-cron via `CONTEXT_GLOBALS_TO_EXCLUDE`.
|
|
270
|
+
|
|
271
|
+
**Recommended: 2 + 1.**
|
|
192
272
|
|
|
193
273
|
### The quotable reference — base-32 over an unambiguous alphabet (reworked 2026-08-04)
|
|
194
274
|
|
|
@@ -320,12 +400,22 @@ in `_underscore` and `library` in the **same** release, and the `/errors` consol
|
|
|
320
400
|
form. **Evaluation order matters:** in `IssueAreaOwners`, `consecutiveCount` is assigned
|
|
321
401
|
**before** `clickupUserId` so the `IF()` compares against the *old* owner. Reversing them
|
|
322
402
|
makes the condition always true and the counter never resets.
|
|
323
|
-
- **`
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
403
|
+
- **`Event.context` is a curated allowlist PLUS a separately-sanitized `GLOBALS` group** (as of
|
|
404
|
+
2026-08-04 — see the GLOBALS section above; the earlier "never capture `$GLOBALS`" position is
|
|
405
|
+
**superseded**). The original `print_r($GLOBALS)` dump it replaced was filtered by a key-name
|
|
406
|
+
blocklist that recursed **arrays only** and walked straight past objects whose *properties* hold
|
|
407
|
+
DB credentials — that is why capture was refused until `sanitizeForContext()` gained Reflection
|
|
408
|
+
descent into private/protected properties. `requestUri` and `referer` have query strings stripped
|
|
409
|
+
(a `?token=abc` was `[REDACTED]` under `get` and stored in clear in `requestUri`). The
|
|
410
|
+
sensitive-fragment list covers **PII**, not just credentials — but only **by key name**, so
|
|
411
|
+
value-recognisable PII still gets through (see the security note above). Persisted traces are
|
|
412
|
+
rebuilt **argument-free**.
|
|
413
|
+
- **`CONTEXT_SENSITIVE_KEY_FRAGMENTS` must include `dsn` and `connection`.** Added 2026-08-04
|
|
414
|
+
alongside `passphrase` and `salt`, in **both** frameworks. A property named `dsn` holding
|
|
415
|
+
`mysql:host=…` came through in clear on the hostile fixture — **a DSN embeds `user:password`**.
|
|
416
|
+
It had never mattered before because under the allowlist-only context no global ever reached the
|
|
417
|
+
redactor at all. Any new fragment must be added to `_underscore/Error.php` **and**
|
|
418
|
+
`library/app/error/capture.php` together.
|
|
329
419
|
- **Keep the debug `print_r($GLOBALS)` dump inside the `isDebugMode()` branch.** It was being
|
|
330
420
|
built on every exception — including the OOM path the memory reserve exists to protect.
|
|
331
421
|
- **Strict-mysqli varchar overflow silently drops the row.** `_Error::initialize()` sets
|
|
@@ -430,6 +520,31 @@ clientUserId). **Neither was built.** As built instead:
|
|
|
430
520
|
|
|
431
521
|
## Change history
|
|
432
522
|
|
|
523
|
+
- 2026-08-04 (latest, **uncommitted/undeployed** at time of writing) — **DECISION REVERSED:
|
|
524
|
+
`$GLOBALS` IS now captured**, as a new top-level `GLOBALS` group, in both frameworks. The earlier
|
|
525
|
+
entry recording that `$GLOBALS` would deliberately never be captured is **superseded and has been
|
|
526
|
+
corrected in place** — its rationale (a key-name blocklist recurses arrays only and steps past
|
|
527
|
+
framework singletons holding DB credentials in object properties) is now recorded as *the reason
|
|
528
|
+
the sanitizer had to be rewritten*. Reversed because our context was compared against Sentry's
|
|
529
|
+
"Globals" section and carried none of it; the variable that explains a failure is routinely one
|
|
530
|
+
nobody thought to allowlist, and 1.0 crons keep almost all state in globals. Made safe by a new
|
|
531
|
+
`sanitizeForContext()` that **recurses into objects via Reflection including private/protected
|
|
532
|
+
properties** (not `get_object_vars()` — a credential is likelier private, and a sanitizer that
|
|
533
|
+
cannot see a value cannot redact it), with depth cap 6, a 2000-char string cap that appends the
|
|
534
|
+
true byte length, superglobals + `GLOBALS`/`appRoot`/`newAppRoot`/`argv`/`argc` excluded,
|
|
535
|
+
Closure/resource placeholders, and `GLOBALS` **shed first** by `encodeContext()` past
|
|
536
|
+
`CONTEXT_MAX_LENGTH`. Verified against a hostile fixture. **Known limitation: global scope only** —
|
|
537
|
+
PHP exposes no API for another frame's locals, so 2.0 requests capture comparatively little.
|
|
538
|
+
**Fixed:** `dsn`, `connection`, `passphrase`, `salt` added to `CONTEXT_SENSITIVE_KEY_FRAGMENTS` in
|
|
539
|
+
both frameworks — a `dsn` property came through in clear on the fixture and a DSN embeds
|
|
540
|
+
`user:password`. **Changed:** every top-level `Event.context` key is now **UPPERCASE**
|
|
541
|
+
(`Reference`→`REFERENCE`, `Runtime`→`RUNTIME`); readers must tolerate **four** coexisting shapes
|
|
542
|
+
for the 7-day retention window. **Recorded as an OPEN security note (not solved):** key-name
|
|
543
|
+
redaction cannot recognise PII by value, so `shippingName`/`shippingAttention`/`shippingCity`/
|
|
544
|
+
`shippingStateCode`/`shippingZip`/`mitsClientId` are still stored into a table every client shares
|
|
545
|
+
and mirrored into ClickUp bodies and escalation emails — the same exposure closed earlier the same
|
|
546
|
+
day in the Compass MITS transmit cron, knowingly reopened at the developer's direction. Three
|
|
547
|
+
mitigations offered, none applied. (jcardinal)
|
|
433
548
|
- 2026-08-04 — **Correction/diagnostic:** recorded that the `dbchanges2/Logs/` migration files lag
|
|
434
549
|
the deployed schema — `2026-07-30a` describes a `code` identity column, but live `Logs.Issue` is
|
|
435
550
|
keyed on **`issueKey` (varchar 128)** — and listed the live column set. Query
|
|
@@ -9,4 +9,4 @@
|
|
|
9
9
|
| [Primary Table templates (server/client, sizing, virtualization)](features/primary-table-templates.md) | `src/templates/PrimaryTable/` is the **production, wired-up table** built on the [Table component](table.md). | toga-blox/src/templates/PrimaryTable/PrimaryTable.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableServerTemplate.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableClientTemplate.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableHeaderCell.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableBodyCell.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableRow.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableExpandableRow.tsx, toga-blox/src/templates/PrimaryTable/types.ts |
|
|
10
10
|
| [TableRecordModal (record-detail modal shell)](features/table-record-modal.md) | `TableRecordModal` is a **generic, presentational modal shell** for showing a single table record (row) in detail — typically opened from a table row click. | toga-blox/src/components/TableRecordModal/TableRecordModal.tsx, toga-blox/src/components/TableRecordModal/index.ts, toga-blox/src/components/TableRecordModal/tableRecordModal.module.css |
|
|
11
11
|
| [Table component (cells, action cells, filters & sorts, hooks, theming)](features/table.md) | The `Table` component (`src/components/Table/`) is the **TanStack Table v8** building block behind the [Primary Table templates](primary-table-templates.md). | toga-blox/src/components/Table/index.ts, toga-blox/src/components/Table/types.ts, toga-blox/src/components/Table/utils/buildTanstackColumns.tsx, toga-blox/src/components/Table/utils/resolveCellType.tsx, toga-blox/src/components/Table/components/cellTypes, toga-blox/src/components/Table/components/actionCells, toga-blox/src/components/Table/components/columnFiltersAndSorts, toga-blox/src/components/Table/hooks, toga-blox/src/components/Table/themeConfig |
|
|
12
|
-
| [Dynamic npm Publish Pipeline (branch → channel)](workflows/dynamic-publish-pipeline.md) | How `@agilant/toga-blox` (checkout folder `toga-blox-npm`, registry repo key `toga-blox`) publishes a per-environment npm **channel** (dist-tag) from a `_<mode> | toga-blox/.github/workflows/publish.yml, toga-blox/src/utils/getFontAwesomeIcon.tsx |
|
|
12
|
+
| [Dynamic npm Publish Pipeline (branch → channel)](workflows/dynamic-publish-pipeline.md) | How `@agilant/toga-blox` (checkout folder `toga-blox-npm`, registry repo key `toga-blox`) publishes a per-environment npm **channel** (dist-tag) from a `_<mode> | toga-blox/.github/workflows/publish.yml, toga-blox/package.json, toga-blox/src/utils/getFontAwesomeIcon.tsx |
|
|
@@ -6,10 +6,11 @@ project: TOGa Blox
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-08-04
|
|
10
10
|
owners: [jcardinal, apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- toga-blox/.github/workflows/publish.yml
|
|
13
|
+
- toga-blox/package.json
|
|
13
14
|
- toga-blox/src/utils/getFontAwesomeIcon.tsx
|
|
14
15
|
related:
|
|
15
16
|
- ../architecture.md
|
|
@@ -99,11 +100,36 @@ environment needs its own maintained `_<mode>` branch in `toga-blox-npm`.
|
|
|
99
100
|
`React.CSSProperties & Record<\`--${string}\`, string>` — the value type must be `string`
|
|
100
101
|
(**not** `string | number`; `CSSVariables` requires `string`). Runtime and function signature
|
|
101
102
|
are unchanged, so it is safe for the ~40 consumers.
|
|
103
|
+
- **The `_production`/`_beta` build copies only `.scss` — a plain `.css` component
|
|
104
|
+
stylesheet is silently dropped from the tarball.** On `_production` and `_beta` the
|
|
105
|
+
`build` script is `tsc && copyfiles -u 1 "src/**/*.scss" dist`: `tsc` compiles a
|
|
106
|
+
component's `import "./Foo.css"` into the emitted JS, but `copyfiles` never ships the
|
|
107
|
+
`.css`, so the published package contains JS importing a file that isn't in the tarball.
|
|
108
|
+
Consumers then fail their Vite/Rollup build with `Could not resolve "./Foo.css"`
|
|
109
|
+
(seen with `EnvironmentBadge` in `@agilant/toga-blox@beta` 1.0.316-beta.108, breaking
|
|
110
|
+
`toga2-commerce`). **Convention on the prod/beta build: author component stylesheets as
|
|
111
|
+
`.scss`, never `.css`** (a flat `.css` file is valid SCSS as-is, so renaming is the
|
|
112
|
+
zero-risk fix — no build-script change). Note the build description in `architecture.md`
|
|
113
|
+
(copies `*.scss/*.css/*.module.css`) reflects the in-progress `feature-new-table` branch,
|
|
114
|
+
**not** the current `_production`/`_beta` build; that feature branch is not to be merged
|
|
115
|
+
to prod/beta just to pick up the wider copy glob.
|
|
116
|
+
- **Diagnose what a channel actually shipped with `npm pack`.** To see exactly which files a
|
|
117
|
+
published dist-tag contains, run `npm pack @agilant/toga-blox@<tag>` then
|
|
118
|
+
`tar tzf <tarball>` — this is how the missing `.css` (JS importing an unshipped stylesheet)
|
|
119
|
+
was confirmed against the tarball rather than the source tree.
|
|
102
120
|
- **Secret hygiene done right.** `publish.yml` writes a transient `.npmrc` at build time from
|
|
103
121
|
GitHub secrets (`${NODE_AUTH_TOKEN}` / FontAwesome token) — it never commits tokens. This is
|
|
104
122
|
the pattern the consuming apps should follow (see the commerce workflow's secret-hygiene note).
|
|
105
123
|
|
|
106
124
|
## Change history
|
|
125
|
+
- 2026-08-04 — Recorded the prod/beta `.scss`-only build gotcha: the `_production`/`_beta`
|
|
126
|
+
`build` copies only `src/**/*.scss`, so a component shipped with a plain `.css` + matching
|
|
127
|
+
`import "./X.css"` publishes JS referencing a stylesheet absent from the tarball, breaking
|
|
128
|
+
consumers' Vite build (`Could not resolve "./EnvironmentBadge.css"`, `@agilant/toga-blox@beta`
|
|
129
|
+
1.0.316-beta.108 → `toga2-commerce`). Fix was renaming `EnvironmentBadge.css`→`.scss` (no
|
|
130
|
+
build-script change), republished on `_beta` as 1.0.316-beta.115 and a `fix/env-badge-scss-prod`
|
|
131
|
+
PR into `_production`; `toga2-commerce` `_beta` bumped to the fixed version. Added the
|
|
132
|
+
`npm pack`/`tar tzf` tarball-inspection diagnostic. (apeterson)
|
|
107
133
|
- 2026-07-23 — Documented CI version derivation (strip suffix → patch+1 → append `-<mode>.<run>`;
|
|
108
134
|
dev owns only the base version, never the suffix) and the `_sandbox-client` deploy procedure now
|
|
109
135
|
driven by the `deploy-sandbox-client` project skill. Recorded that `publish.yml` has diverged
|
|
@@ -348,6 +348,14 @@ this controller needs the same treatment.**
|
|
|
348
348
|
|
|
349
349
|
## Gotchas / known issues
|
|
350
350
|
|
|
351
|
+
- **⚠ This cron is an EXFILTRATION PATH for `Event.context` — treat anything captured as
|
|
352
|
+
ticket-visible and email-visible.** As of 2026-08-04 capture writes a `GLOBALS` group into
|
|
353
|
+
`Event.context`, and redaction is **by key name only**, so value-recognisable PII (names, city,
|
|
354
|
+
state, ZIP, publishable-looking ids) survives into ClickUp task bodies and escalation emails —
|
|
355
|
+
neither of which is single-tenant or access-controlled the way Sentry was. Before widening what
|
|
356
|
+
this cron copies out of `context`, read the security note in the
|
|
357
|
+
[2.0 error-reporting doc](../../_underscore/features/error-reporting-issue-event.md). Mitigations
|
|
358
|
+
are proposed but **not applied**.
|
|
351
359
|
- **An issue held at HIGH purely by neglect never de-escalates on its own.**
|
|
352
360
|
`applyDeEscalationDwell()` needs the computed target to sit **below** the stored urgency for
|
|
353
361
|
`REQUIRED_QUIET_RUNS` (3) consecutive runs, but the neglect axis keeps recomputing the target
|
package/package.json
CHANGED