toga-ai 1.0.524 → 1.0.526

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.
@@ -95,6 +95,62 @@ curated text, affected clients) right, with an always-present Type chip.
95
95
  - Header chip order is **Issue / Environment / Type / Urgency** — Type was moved ahead of Urgency so
96
96
  urgency finishes the row at the far right.
97
97
 
98
+ ### Issue-detail layout rules (2026-08-04)
99
+
100
+ **⚠ Never size anything inside `.issue-split-main` with a viewport media query.** The context
101
+ cards live inside the app sidebar *and* beside the curation aside, so the grid gets only a
102
+ fraction of the window — at a 1240px viewport the grid has roughly **540px**. A `min-width: 1180px`
103
+ media query happily drew **three** columns into it. A viewport query cannot see the chrome around
104
+ the element; **any grid in the two-column issue layout must break on its own width.**
105
+
106
+ The context grid does that with no media queries, no container queries and no markup change:
107
+
108
+ ```css
109
+ --context-card-min: 480px;
110
+ grid-template-columns: repeat(auto-fit,
111
+ minmax(min(100%, max(var(--context-card-min), (100% - 28px) / 3)), 1fr));
112
+ ```
113
+
114
+ The floor is `max(readable minimum, one third of the available width)`, measured against the grid
115
+ itself: **<974px → 1 column, 974px+ → 2, 1468px+ → 3, and never 4**, because past that point the
116
+ one-third term wins and grows with the container. `min(100%, …)` stops the floor exceeding the
117
+ container on a phone; `28px` is the two 14px gaps three columns cost. The previous
118
+ `repeat(auto-fit, minmax(320px, 1fr))` packed in **five** columns on a wide monitor (this page is
119
+ full-bleed — `.app-content` `max-width` is overridden to `none`), leaving every card too narrow for
120
+ its own content, so `SERVER`'s paths and `RUNTIME`'s `include_path` wrapped to four lines and each
121
+ card grew its own scrollbar.
122
+
123
+ `.context-card-body` `max-height` went **340px → 510px** — at 340px even a modest group scrolled, so
124
+ an inner scrollbar was the normal state. The ceiling stays so one enormous group (`SERVER`, or the
125
+ `GLOBALS` dump) cannot set the height of the whole board.
126
+
127
+ **The CONTEXT heading names which occurrence it came from** — `CONTEXT 1Q-557 at 2026-08-04
128
+ 18:07:04`: the event reference (the same identifier the Events table uses) plus that event's
129
+ `dtCreated`, in the subtle text token with the reference in semibold mono. This matters because the
130
+ context and stack trace are the **most recent** event's — an issue that has fired 700 times has 700
131
+ different contexts, and nothing on the page previously said which one you were reading.
132
+
133
+ Two implementation constraints worth keeping:
134
+
135
+ - It is a **flex row (`.context-heading`) wrapping the `h3` and the caption**, baseline-aligned —
136
+ *not* a `<span>` inside the `h3`, which inherits `text-transform: uppercase` and the heavier
137
+ weight and would have rendered "1Q-557 AT …". `flex-wrap` lets the caption drop to its own line
138
+ in a narrow pane.
139
+ - **⚠ GENERAL RULE: any heading in this console that gets wrapped in a flex or grid container loses
140
+ its full-width section rule and must hand it to the wrapper.** `.tool--errors h3` carries the
141
+ section rule as its own `border-top`; once the `h3` became a flex *item* it shrank to its content
142
+ width and the rule stopped mid-page after the word "Context". The `margin`/`padding-top`/
143
+ `border-top` moved up to the full-width `.context-heading` and are zeroed on the nested `h3`.
144
+
145
+ The caption renders only inside the branch where the latest event is non-null — an array offset on
146
+ null in 1.0 becomes a rendered red error page.
147
+
148
+ **"← All issues" is a button**, built on the existing `.btn-secondary-outline` so it inherits the
149
+ console's button vocabulary. The arrow is a separate span moved with `transform` **only** (animating
150
+ a gap or margin reflows the label and makes the control twitch), inside a `prefers-reduced-motion`
151
+ guard where the colour change still carries the hover. It is the only navigation control on the page
152
+ and was previously styled as body copy, indistinguishable from an inline reference to another issue.
153
+
98
154
  ### Neglect indicator (2026-08-04)
99
155
 
100
156
  Both pages now surface the **neglect** contribution to urgency: the listing computes
@@ -117,6 +173,11 @@ second line — the two halves then read as unrelated fragments. It is now `disp
117
173
  > so `nowrap` alone would have traded a wrap for an overflow. `col-urgency` went **96px → 136px**
118
174
  > and `.issue-listing` `min-width` **1026px → 1066px** to match. Any future `nowrap` in this table
119
175
  > needs the same paired column-width change.
176
+ >
177
+ > **Hit twice in one day.** The detail page's fingerprint hash was `wrap-anywhere` and broke as
178
+ > `424b20ed7` / `5`, reading as two values; the fix is `nowrap` **plus** `.col-fp-hash` **74px →
179
+ > 92px** (ten monospace characters at 11px need ~78px plus cell padding). The full hash stays on the
180
+ > `title` attribute. **Widening the column is part of the fix, never optional.**
120
181
 
121
182
  **Deliberately scoped to the neglect contribution only.** The console never claims *which* axis is
122
183
  winning: computing the full driving axis in Tools would mean copying the volume, sustained and
@@ -236,7 +297,27 @@ They were renamed from plural on 2026-08-01, after the pipeline was already in p
236
297
 
237
298
  ## Change history
238
299
 
239
- - 2026-08-04 (latest, **uncommitted/undeployed** at time of writing) — **Fixed:** the neglect
300
+ - 2026-08-04 (latest, **uncommitted/undeployed**; CSS/markup/nav only — no PHP logic, schema or
301
+ worker change) — **Fixed:** the context cards were too narrow and too short. `.context-card-body`
302
+ `max-height` 340px → 510px, and the grid's `repeat(auto-fit, minmax(320px, 1fr))` (five columns on
303
+ a wide full-bleed monitor) replaced by a floor of
304
+ `max(--context-card-min: 480px, (100% - 28px) / 3)` wrapped in `min(100%, …)` — 1/2/3 columns at
305
+ 974px/1468px of **grid** width, never four. **A first attempt using viewport media queries was
306
+ wrong and is the durable lesson:** these cards sit inside `.issue-split-main`, so a 1180px viewport
307
+ query drew three columns into a ~540px grid; anything in the two-column issue layout must break on
308
+ its own width. Verified by computing the column count across eleven grid widths against the
309
+ developer's screenshots. **Fixed:** the fingerprint hash wrapped to two lines — `nowrap` **with**
310
+ `.col-fp-hash` 74px → 92px, the same `table-layout: fixed` nowrap/column-width pairing hit earlier
311
+ today on the neglect note. **Built:** the CONTEXT heading now names the occurrence it came from
312
+ (`1Q-557 at 2026-08-04 18:07:04`), since the context shown is the most recent event's and nothing
313
+ said which one; implemented as a `.context-heading` flex row rather than a span inside the
314
+ uppercase `h3`, which forced the section `border-top` up onto the full-width wrapper — a heading
315
+ made into a flex item loses its full-width rule. **Built:** "← All issues" is now a
316
+ `.btn-secondary-outline` button with a `transform`-only arrow shift under a
317
+ `prefers-reduced-motion` guard. **Changed:** the sidebar's single-link *Error Reporting* section
318
+ was folded into *Developers* as **Errors** (see
319
+ [persona-gated-navigation](./persona-gated-navigation.md)) — no access change. (jcardinal)
320
+ - 2026-08-04 (earlier, **uncommitted/undeployed** at time of writing) — **Fixed:** the neglect
240
321
  indicator wrapped onto two lines in the listing — `.neglect-note` was `display: inline-block`, so
241
322
  "neglect NORMAL · 26h" broke after "neglect" and read as two unrelated fragments. Now
242
323
  `display: block` + `white-space: nowrap`, **with `col-urgency` 96px → 136px and `.issue-listing`
@@ -6,7 +6,7 @@ project: Tools
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-06-26
9
+ updated: 2026-08-04
10
10
  owners: [jcardinal]
11
11
  files:
12
12
  - tools/_/app/nav.php
@@ -14,6 +14,7 @@ files:
14
14
  related:
15
15
  - ../architecture.md
16
16
  - ./saml-sso-auth.md
17
+ - ./errors-curation-console.md
17
18
  ---
18
19
 
19
20
  ## Summary
@@ -60,6 +61,24 @@ rather than a blank page:
60
61
  This is intentional: a valid user is never shown a broken/blank dashboard, and access is granted
61
62
  purely by assigning personas — no code change is needed to grant a user access to an existing tool.
62
63
 
64
+ ### Avoid single-link folders
65
+
66
+ A folder that holds exactly one action costs a whole rail section for one link. The
67
+ **Error Reporting** folder was removed on 2026-08-04 and its action moved to the top of
68
+ **Developers** (above Generate UUID and Generate Password), relabelled **Issues → Errors**.
69
+
70
+ > **Moving an action between folders can silently change who sees it** — visibility is the folder
71
+ > gate plus the action gate. This move did not, because both folders carried identical personas
72
+ > `['Development Team', 'TOGa Technology']`. Both `/errors` views also hardcode
73
+ > `App_Auth::requireAuth(['TOGa Technology', 'Development Team'])` rather than calling
74
+ > `personasForRoute()` — deliberate, per the folder-union warning in `nav.php`. **Check both gates
75
+ > before moving an action.**
76
+
63
77
  ## Change history
78
+ - 2026-08-04 (**uncommitted/undeployed** at time of writing) — Removed the single-link *Error
79
+ Reporting* folder; its action moved to the top of *Developers* and relabelled *Issues* → *Errors*.
80
+ No access change (both folders carried identical personas, and the `/errors` views hardcode their
81
+ own `requireAuth`). Recorded that moving an action between folders can change visibility via the
82
+ folder gate. (jcardinal)
64
83
  - 2026-06-26 — Documented the authorization model: authentication (SSO) and authorization (persona-gated nav) are separate — any valid user logs in regardless of personas; a persona-less user gets an empty left rail and an explicit dashboard empty-state ("no tools available", contact-dev card + Log out) instead of a blank page (jcardinal)
65
84
  - 2026-06-25 — Built App_Nav: hard-coded two-level persona-gated navigation; render() for the left rail, visibleGroups() for the dashboard, personasForRoute() for page self-guarding; route maps 1:1 to mvc/<route>/get.php (jcardinal)
@@ -6,7 +6,7 @@ project: TOGa 2.5 Supply
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-08-04
9
+ updated: 2026-08-05
10
10
  owners: [jcardinal, apeterson]
11
11
  files:
12
12
  - toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/sectionRenderers.tsx
@@ -338,6 +338,33 @@ chrome (title, reason-input required-ness, confirm button, icons, toasts) diverg
338
338
  surfaces (`sales-order-approve-action`, `sales-order-deny-action`). Seed side:
339
339
  [surface-layer-schema](../../dbchanges2/features/surface-layer-schema.md).
340
340
 
341
+ ## Approval-details card — staged (approval-chain) value resolution via `config.preferredStage`
342
+
343
+ The record-modal **approval-details** card resolves several fields off a *specific approval stage*
344
+ rather than off the order root — e.g. **Assigned Manager** must show the same stage-2 manager the
345
+ approval-decision modal shows. The surface declares this with a `config.preferredStage` marker on the
346
+ element (surface-owned binding); the FE owns the *resolution mechanics*.
347
+
348
+ - The approval-details card uses a **different adapter** than the decision modal —
349
+ `SalesOrders/helpers/surfaceBundleToTenantFields.ts` — which originally had no staged resolution,
350
+ so `preferredStage`-bound values came back blank. The staged resolver was **ported from the decision
351
+ adapter** (`surfaceBundlesToDecisionFields.ts`): `resolvePathInsensitive` + `makePreferredStageResolver`.
352
+ - When `config.preferredStage` is set, the adapter attaches a **`field.getValue(order)`** that resolves
353
+ the `valueKey` from `order.stages[step === preferredStage]` instead of the order root. The record
354
+ modal's order carries `stages` via `useSalesOrderRecordModalLayoutModel`. `DetailSection.js` (blox)
355
+ honors `field.getValue`. Staged `getValue` is attached to the **primary** value, the **`labelSuffix`**
356
+ (VIP badge), and the **`isConcatenated`** appended value (inline email).
357
+ - **blox dependency (staged concatenated email).** The inline-email `isConcatenated` staged value only
358
+ renders once blox `renderBaseDetailFieldValue.tsx` / `DetailSection/types.ts` accept an optional
359
+ `getValue` on the `isConcatenated` descriptor (previously `isConcatenated` dot-path resolved only off
360
+ the record root — a general off-root seam, backward compatible). That blox change is on branch
361
+ `feature-new-table` and is **NOT yet published**; the Assigned Manager staged email stays blank until
362
+ a toga-blox publish + version bump in toga25-supply.
363
+ - **Per-client driver.** Compass + Compass Canada set `preferredStage:'2'` via a `CONFIG` SurfaceOverride
364
+ on the approval-details `assignedManager` element (whole-blob replace — see resolver override
365
+ semantics). Quad overrides field sort/visibility on the same card. Core seeds the shared content
366
+ (shipping-method + currency-charge concat, Order-For inline email path).
367
+
341
368
  ## Two FE rule engines (surface Tier-1 vs legacy named-rule) — do not conflate
342
369
 
343
370
  There are **two** rule evaluators in this app. As of 2026-07-21 the surface evaluator now has a
@@ -544,6 +571,15 @@ FE/CSS; content/visibility/order/ACL → Surface.**
544
571
  treat type-checking as pending. Runtime `GET /v2/surfaces/{slug}/meta` also not yet exercised.
545
572
 
546
573
  ## Change history
574
+ - 2026-08-05 — **Built/Fixed** staged approval-chain value resolution on the record-modal
575
+ **approval-details** card. Ported the decision adapter's staged resolver (`resolvePathInsensitive`
576
+ + `makePreferredStageResolver`) into `surfaceBundleToTenantFields.ts`: a surface `config.preferredStage`
577
+ marker now drives a `field.getValue` that resolves the `valueKey` from `order.stages[step===preferredStage]`
578
+ (primary value + `labelSuffix` VIP badge + `isConcatenated` email) so Assigned Manager matches the
579
+ stage-2 decision manager. Blocked on a **pending, unpublished** toga-blox seam (`isConcatenated` gained
580
+ an optional `getValue` for off-root resolution, branch `feature-new-table`) before the staged email
581
+ renders. Per-client: Compass/Compass Canada set `preferredStage:'2'` (CONFIG override); Quad overrides
582
+ sort/visibility; Core seeds shipping-method+charge concat and the Order-For email path. (apeterson)
547
583
  - 2026-08-04 — **Built** a conditional two-layout split for the SalesOrders record modal
548
584
  (approval vs standard), driven by `isApprovalDetailsActive` at `SalesOrderView`. In approval
549
585
  stages the approval-details card leads (wide), Order Details + Bill To move below the Items
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.524",
3
+ "version": "1.0.526",
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",