toga-ai 1.0.510 → 1.0.512
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/tools/INDEX.md +2 -1
- package/knowledge/1.0/apps/tools/architecture.md +24 -2
- package/knowledge/1.0/apps/tools/features/clickup-sprint-dashboard.md +13 -2
- package/knowledge/1.0/apps/tools/features/design-demo-admin.md +1 -2
- package/knowledge/1.0/apps/tools/features/legacy-email-notifier.md +15 -2
- package/knowledge/1.0/apps/tools/features/talos-pricing-ui.md +20 -0
- package/knowledge/1.0/apps/tools/features/theme-light-dark.md +209 -0
- package/knowledge/1.0/standards/frontend.md +42 -3
- package/knowledge/2.0/apps/_underscore/features/error-reporting-issue-event.md +18 -0
- package/knowledge/2.0/apps/worker2/features/error-escalation-cron.md +27 -1
- package/knowledge/INDEX.md +2 -2
- package/knowledge/clients/compass-usa/INDEX.md +1 -0
- package/knowledge/clients/compass-usa/features/mits-sales-order-transmission-alerting.md +89 -0
- package/knowledge/clients/compass-usa/profile.md +7 -2
- package/knowledge/standalone/apps/claude/INDEX.md +1 -0
- package/knowledge/standalone/apps/claude/workflows/ticket-branch-creation-safety.md +91 -0
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
| Doc | Summary | Files |
|
|
4
4
|
|-----|---------|-------|
|
|
5
|
-
| [Tools (1.0 Internal-Tools App) Architecture](architecture.md) | **Tools** is a standalone 1.0 (`App_`) application that houses many small internal tools behind simple interfaces, gated by Client_True staff persona. | tools/index.php, tools/_/app/framework.php, tools/_/app/frameworkindex.php, tools/assets/img/favicon/favicon.ico, tools/assets/img/favicon/favicon-32x32.png, tools/assets/img/favicon/favicon-16x16.png, tools/assets/img/favicon/apple-touch-icon.png, tools/_/app/auth.php, tools/_/app/nav.php, tools/common/header.php, tools/common/footer.php, tools/mvc/get.php, tools/mvc/_TEMPLATE/get.php, tools/docs/ADDING_A_TOOL.md |
|
|
5
|
+
| [Tools (1.0 Internal-Tools App) Architecture](architecture.md) | **Tools** is a standalone 1.0 (`App_`) application that houses many small internal tools behind simple interfaces, gated by Client_True staff persona. | tools/index.php, tools/_/app/framework.php, tools/_/app/frameworkindex.php, tools/assets/img/favicon/favicon.ico, tools/assets/img/favicon/favicon-32x32.png, tools/assets/img/favicon/favicon-16x16.png, tools/assets/img/favicon/apple-touch-icon.png, tools/_/app/auth.php, tools/_/app/nav.php, tools/common/header.php, tools/common/footer.php, tools/mvc/get.php, tools/mvc/_TEMPLATE/get.php, tools/docs/ADDING_A_TOOL.md, tools/assets/css/theme.css, tools/assets/js/theme.js |
|
|
6
6
|
| [ClickUp Sprint Dashboard (tools /clickup/react)](features/clickup-sprint-dashboard.md) | A native **ClickUp sprint dashboard** in the 1.0 `tools` app at route `/clickup/react`, built for a wall-mounted **TV display** ("TOGa IQ" internal sprint metri | tools/_/app/clickup/sprint.php, tools/v2/sprints/tile/index.php, tools/v2/sprints/current/index.php, tools/v2/sprints/status-breakdown/index.php, tools/v2/sprints/worktype-breakdown/index.php, tools/v2/sprints/points-by-dev/index.php, tools/v2/sprints/burndown/index.php, tools/assets/clickup/sprint-dashboard.html, tools/mvc/clickup/react/get.php, tools/mvc/login/get.php, tools/_/app/nav.php |
|
|
7
7
|
| [CloudFront Client Setup](features/cloudfront-client-setup.md) | An SSO-gated admin tool at **`/devops/cloudfront-clients`** in the Tools 1.0 app that onboards a client onto **CloudFront + Route 53 across multiple AWS account | tools/_/app/devops/cloudfront.php, tools/mvc/devops/cloudfront-clients/get.php, tools/mvc/devops/cloudfront-clients/post.php, tools/assets/js/cloudfront-clients.js, tools/assets/css/cloudfront-clients.css, tools/_/app/nav.php, tools/_/app/frameworkindex.php, tools/config.production.ini |
|
|
8
8
|
| [Design Demo Admin](features/design-demo-admin.md) | A self-serve admin UI at **`/design`** in the SSO-protected **Tools** app that lets the design team publish self-contained "Claude Design" HTML exports as **ver | tools/_/app/design/github.php, tools/mvc/design/get.php, tools/mvc/design/post.php, tools/assets/css/design.css, tools/assets/js/design.js, tools/_/app/frameworkindex.php, tools/_/app/nav.php, tools/composer.json |
|
|
@@ -14,4 +14,5 @@
|
|
|
14
14
|
| [Tools SAML SSO Consumer & Persona-Gated Auth (App_Auth)](features/saml-sso-auth.md) | `App_Auth` is the Tools app's authentication layer: it consumes the SAML gateway `?saml=` handoff (see the 2.0 SAML downstream integration contract), establishe | tools/_/app/auth.php, tools/mvc/sso/initiate/get.php, tools/mvc/sso/get.php, tools/mvc/login/get.php, tools/mvc/login/post.php, tools/mvc/logout/get.php, tools/mvc/get.php, tools/config.production.ini, tools/config.local.ini |
|
|
15
15
|
| [Talos Knowledge Base Admin UI (KB Documents + Vocabulary)](features/talos-kb-documents-admin.md) | > **PER-AI-MODEL, DATA-DRIVEN SCOPING (2026-07-29).** The KB-documents and Vocabulary admin > UIs were refactored from a single hard-coded **"development-team"* | tools/mvc/talos/kb-documents/get.php, tools/mvc/talos/kb-documents/post.php, tools/mvc/talos/knowledge-bases/get.php, tools/mvc/talos/knowledge-bases/post.php, tools/mvc/talos/vocabulary/get.php, tools/mvc/talos/vocabulary/post.php, tools/_/app/talos/s3.php, tools/_/app/talos/bedrock.php, tools/_/app/pg.php, tools/_/app/model/true/aimodels.php, tools/_/app/model/true/vectorindexes.php, tools/_/app/model/true/aimodels_vectorindexes.php, tools/_/app/worker.php, tools/_/app/nav.php, tools/config.production.ini, tools/config.alpha.ini, tools/.platform/httpd/conf.d/timeouts.conf, tools/.platform/hooks/prebuild/01-install-php-pgsql.sh, tools/.platform/hooks/postdeploy/01-restart-php.sh |
|
|
16
16
|
| [Talos Pricing UI (Contracts, Pricing Dashboard, Usage, Settings + Estimator)](features/talos-pricing-ui.md) | The 1.0 (tools app) face of the **Talos Pricing Platform** — a "Talos Pricing" nav folder with four pages plus the estimate engine. | tools/_/app/nav.php, tools/_/app/talos/estimator.php, tools/_/app/talos/usage.php, tools/mvc/talos/contracts/get.php, tools/mvc/talos/contracts/post.php, tools/mvc/talos/pricing/get.php, tools/mvc/talos/usage/get.php, tools/mvc/talos/settings/get.php, tools/mvc/talos/settings/post.php, tools/assets/css/style.css |
|
|
17
|
+
| [App-Wide Colour Theme (light / dark / auto)](features/theme-light-dark.md) | A **light / dark / auto** colour theme for the *entire* Tools app, built as a single semantic-token layer (`assets/css/theme.css`) rather than per-page edits. | tools/assets/css/theme.css, tools/assets/js/theme.js, tools/_/app/frameworkindex.php, tools/_/app/nav.php, tools/common/header.php, tools/mvc/login/get.php, tools/assets/css/style.css, tools/assets/css/design.css, tools/assets/css/login.css, tools/assets/css/cloudfront-clients.css, tools/assets/css/gchart.css, tools/mvc/talos/usage/get.php, tools/mvc/talos/pricing/get.php, tools/mvc/talos/contracts/get.php, tools/mvc/clickup/dashboard/get.php, tools/mvc/email-migration/notify/post.php |
|
|
17
18
|
| [Deploying Tools to Elastic Beanstalk (PHP 8.5 / Amazon Linux 2023)](workflows/deploy-to-elastic-beanstalk-al2023.md) | How the **Tools** 1.0 app boots on Elastic Beanstalk running `PHP 8.5 on 64bit Amazon Linux 2023/4.13.1 (aarch64)`. | tools/.ebextensions/004_http_to_https.config, tools/.ebextensions/006_mount-s3fs.config, tools/.ebextensions/007_setup_export_cache_folders.config, tools/.ebextensions/008_setup_ldap.config, tools/.ebextensions/009_setup_phpini.config, tools/.ebextensions/020_setup_git_libraries.config, tools/.ebextensions/050_register_instance_to_shared_application_load_balancer.config, tools/ebs/git.json, tools/ebs/setup_export_cache_folders.php, tools/_/app/frameworkindex.php |
|
|
@@ -6,7 +6,7 @@ project: Tools
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: architecture
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-08-04
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- tools/index.php
|
|
@@ -23,7 +23,10 @@ files:
|
|
|
23
23
|
- tools/mvc/get.php
|
|
24
24
|
- tools/mvc/_TEMPLATE/get.php
|
|
25
25
|
- tools/docs/ADDING_A_TOOL.md
|
|
26
|
+
- tools/assets/css/theme.css
|
|
27
|
+
- tools/assets/js/theme.js
|
|
26
28
|
related:
|
|
29
|
+
- ./features/theme-light-dark.md
|
|
27
30
|
- ./features/saml-sso-auth.md
|
|
28
31
|
- ./features/persona-gated-navigation.md
|
|
29
32
|
- ./features/developer-tools.md
|
|
@@ -47,7 +50,13 @@ double-gated (`[internal] dev_mode` AND `App_Registry::inDevMode()`). `config.*.
|
|
|
47
50
|
(incl. `config.prod.ini`) is committed with plaintext production secrets that were **publicly
|
|
48
51
|
web-readable** until the front-controller deny blocks added 2026-07-24 (see Known issues);
|
|
49
52
|
rotation of every exposed secret is still owed. Never add more secrets, and keep config files
|
|
50
|
-
denied at the front controller / out of the web root.
|
|
53
|
+
denied at the front controller / out of the web root. New CSS must use `--c-*` tokens from
|
|
54
|
+
`assets/css/theme.css`, never raw colour literals. **`assets/css/style.scss` is DEAD — edit
|
|
55
|
+
`style.css` directly** (the `.css` has drifted ~450 lines ahead; there is no sass binary or
|
|
56
|
+
`package.json`, so recompiling the SCSS would silently revert shipped work).
|
|
57
|
+
`App_FrameworkIndex::setHeaderCode()` is the **only** hook for markup in `<head>`, and it
|
|
58
|
+
**SETS rather than appends** — the theme bootstrap is its sole caller, so a second call
|
|
59
|
+
silently clobbers it.
|
|
51
60
|
|
|
52
61
|
## Boot & structure
|
|
53
62
|
|
|
@@ -72,6 +81,14 @@ sibling apps. A fixed left two-column shell: dark left sidebar (`.app-nav`) with
|
|
|
72
81
|
persona-filtered nav in the middle, pinned footer with the user's name + a ghost "Log out"
|
|
73
82
|
button; content area on the right. **No top header bar** (see Critical rules).
|
|
74
83
|
|
|
84
|
+
**All colour comes from a semantic token layer, `assets/css/theme.css`**, registered **first**
|
|
85
|
+
in `App_FrameworkIndex_Tools::__construct()` so every other sheet resolves through it. The app
|
|
86
|
+
supports light / dark / auto; the resolved theme is stamped on `<html>` as `data-theme` by an
|
|
87
|
+
inline pre-paint script and the user's choice lives in `localStorage` (`toga-tools-theme`) —
|
|
88
|
+
**not** the DB. The left sidebar and public header stay dark in **both** themes, which is why
|
|
89
|
+
`toga-brandmark-white.png` still works everywhere. Login shows `toga-horizontal-colored.png` in
|
|
90
|
+
light and `toga-horizontal-white.png` in dark. See `features/theme-light-dark.md`.
|
|
91
|
+
|
|
75
92
|
## Home route
|
|
76
93
|
|
|
77
94
|
`/` (`mvc/get.php`) renders a persona-filtered dashboard of available tools (tiles grouped by
|
|
@@ -106,6 +123,11 @@ Create `mvc/<folder>/<tool>/get.php` (copy `mvc/_TEMPLATE/get.php`), add one ent
|
|
|
106
123
|
composer package must `require_once` the autoloader itself.
|
|
107
124
|
|
|
108
125
|
## Change history
|
|
126
|
+
- 2026-08-04 — Added the app-wide light/dark/auto theme: `assets/css/theme.css` token layer
|
|
127
|
+
registered first in `App_FrameworkIndex_Tools`, pre-paint `data-theme` stamping via the
|
|
128
|
+
one-and-only `setHeaderCode()` hook, `localStorage` preference (no DB column), nav/public
|
|
129
|
+
header dark in both themes. Recorded that `style.scss` is dead and `setHeaderCode()`
|
|
130
|
+
sets-not-appends. Not committed to `tools` yet (jcardinal)
|
|
109
131
|
- 2026-07-24 — Fixed the config-file web-root exposure: `config.*.ini` was publicly readable via
|
|
110
132
|
the `!-f`-only front controller (`GET /config.production.ini` served it verbatim); added
|
|
111
133
|
`<FilesMatch>` deny blocks referencing the new 1.0 back-end security standard. Softened the
|
|
@@ -6,8 +6,8 @@ project: Tools
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: [kyalamarthi]
|
|
9
|
+
updated: 2026-08-04
|
|
10
|
+
owners: [kyalamarthi, jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- tools/_/app/clickup/sprint.php
|
|
13
13
|
- tools/v2/sprints/tile/index.php
|
|
@@ -24,6 +24,7 @@ related:
|
|
|
24
24
|
- ./persona-gated-navigation.md
|
|
25
25
|
- ./saml-sso-auth.md
|
|
26
26
|
- ./mvc-data-access-patterns.md
|
|
27
|
+
- ./theme-light-dark.md
|
|
27
28
|
- ../architecture.md
|
|
28
29
|
- ../../../2.0/apps/worker2/features/team-sprint-management.md
|
|
29
30
|
---
|
|
@@ -138,5 +139,15 @@ the React dashboard supersedes it.
|
|
|
138
139
|
passed by reference", which the E_ALL error handler promotes to a fatal 500. Assign first:
|
|
139
140
|
`$res = App_Database::query(...); buildArrayOfRows($res, false);`.
|
|
140
141
|
|
|
142
|
+
- **The old Power BI embed page (`mvc/clickup/dashboard/get.php`) keeps ~6 fixed colour
|
|
143
|
+
literals on purpose — do NOT theme them.** Its card/frame colour `#0b1020` is matched to the
|
|
144
|
+
embedded Power BI report canvas so nothing flashes white while the iframe loads. Themed
|
|
145
|
+
surfaces there would reintroduce the flash in one theme or the other. Everything else on the
|
|
146
|
+
page was tokenised for the app-wide theme — see
|
|
147
|
+
[App-Wide Colour Theme](./theme-light-dark.md).
|
|
148
|
+
|
|
141
149
|
## Change history
|
|
150
|
+
- 2026-08-04 — Tokenised the `/clickup/dashboard` page-local styles for the app-wide light/dark
|
|
151
|
+
theme, keeping ~6 deliberate fixed literals (incl. the `#0b1020` frame that matches the
|
|
152
|
+
embedded Power BI canvas so the iframe load does not flash white). Not committed. (jcardinal)
|
|
142
153
|
- 2026-07-29 — Built `/clickup/react` as a native tools feature (data engine, `/v2/sprints/*` routes, CDN-React TV UI, iframe embed, "Dashboard/Clickup" nav); removed the old `/clickup/dashboard` Power BI nav entry; documented two reusable 1.0 gotchas (queryLogging preflight on Defaults-less DBs, buildArrayOfRows by-ref). (kyalamarthi)
|
|
@@ -208,5 +208,4 @@ None — internal/shared design-team tool.
|
|
|
208
208
|
- [Tools Architecture](../architecture.md)
|
|
209
209
|
- [Static Demo Hosting](../../../standalone/apps/forward/features/static-demo-hosting.md)
|
|
210
210
|
- [Deploying Tools to Elastic Beanstalk (AL2023)](../workflows/deploy-to-elastic-beanstalk-al2023.md)
|
|
211
|
-
|
|
212
|
-
</invoke>
|
|
211
|
+
|
|
@@ -6,14 +6,15 @@ project: Tools
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
10
|
-
owners: [bala]
|
|
9
|
+
updated: 2026-08-04
|
|
10
|
+
owners: [bala, jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- tools/mvc/email-migration/notify/get.php
|
|
13
13
|
- tools/mvc/email-migration/notify/post.php
|
|
14
14
|
- tools/_/app/nav.php
|
|
15
15
|
related:
|
|
16
16
|
- ./mvc-data-access-patterns.md
|
|
17
|
+
- ./theme-light-dark.md
|
|
17
18
|
- ../workflows/deploy-to-elastic-beanstalk-al2023.md
|
|
18
19
|
- ../architecture.md
|
|
19
20
|
---
|
|
@@ -104,7 +105,19 @@ endpoint that swallows non-fatal diagnostics (returns `true`) for the duration o
|
|
|
104
105
|
fatals and thrown exceptions still reach the `catch`. The framework mechanism is documented in
|
|
105
106
|
[mvc-data-access-patterns](./mvc-data-access-patterns.md).
|
|
106
107
|
|
|
108
|
+
## Gotcha — the colour literals in `post.php` are EMAIL HTML; never tokenise them
|
|
109
|
+
|
|
110
|
+
`post.php` builds the digest **email** body. Email clients cannot resolve CSS custom
|
|
111
|
+
properties, so its colour literals must stay hard-coded — a `var(--c-…)` there renders as no
|
|
112
|
+
colour at all. This is easy to get wrong because the on-screen `get.php` in the same folder
|
|
113
|
+
*was* tokenised for the app-wide theme (see
|
|
114
|
+
[App-Wide Colour Theme](./theme-light-dark.md)). Rule: styles that leave the browser are not
|
|
115
|
+
themed.
|
|
116
|
+
|
|
107
117
|
## Change history
|
|
118
|
+
- 2026-08-04 — Tokenised the on-screen `get.php` page styles for the app-wide light/dark theme
|
|
119
|
+
and recorded that `post.php`'s email-HTML colour literals must stay hard-coded (email clients
|
|
120
|
+
cannot resolve CSS custom properties). Not committed. (jcardinal)
|
|
108
121
|
- 2026-08-03 — Built the Legacy Email Notifier: multi-file xlsx merge via vendored PhpSpreadsheet,
|
|
109
122
|
legacy-domain filter, `Client_True.Users` local-part resolution with always-domain-swap
|
|
110
123
|
(never-skip), one digest per person per legacy domain, and chunked (25/batch) browser-driven
|
|
@@ -20,6 +20,7 @@ files:
|
|
|
20
20
|
- tools/mvc/talos/settings/post.php
|
|
21
21
|
- tools/assets/css/style.css
|
|
22
22
|
related:
|
|
23
|
+
- ./theme-light-dark.md
|
|
23
24
|
- ./persona-gated-navigation.md
|
|
24
25
|
- ./mvc-data-access-patterns.md
|
|
25
26
|
- ../architecture.md
|
|
@@ -97,6 +98,20 @@ about cost; cost is driven by tool-call **intensity** via the profile
|
|
|
97
98
|
- **Voice multipliers (0.25 / 0.50) are UNVALIDATED** — supplied second-hand, never measured,
|
|
98
99
|
and possibly inverted. Flagged in the UI. **Do not quote voice from them.**
|
|
99
100
|
- **`db_team`** is read/write on Contracts and Settings, read-only on the dashboards.
|
|
101
|
+
- **Chart.js colours must come from theme tokens, and a live theme switch needs
|
|
102
|
+
`chart.update()`.** These pages now read series/axis/tick/gridline colours via
|
|
103
|
+
`TogaTheme.color('--c-…')` inside a `paint()` registered with `TogaTheme.onChange(paint)`,
|
|
104
|
+
then call `chart.update()` — `update()` clears Chart.js's resolver cache, so without it a
|
|
105
|
+
`Chart.defaults` change does not reach charts that already exist. Chart.js otherwise defaults
|
|
106
|
+
axis/tick text to a dark colour that is unreadable in dark mode. See
|
|
107
|
+
[App-Wide Colour Theme](./theme-light-dark.md).
|
|
108
|
+
- **These pages keep an explicit `--c-bs-*` token group** because they were authored against
|
|
109
|
+
Bootstrap's palette (`#0d6efd`, `#6c757d`, `#495057`, `#dc3545`, `#198754`…), which is close
|
|
110
|
+
to but not the TOGA brand palette — folding them into `--c-primary` visibly restyles them.
|
|
111
|
+
- **Open (dark mode):** the error toast measures ~2.4:1 (white on `--c-danger`) and likely
|
|
112
|
+
wants a fixed dark fill; the Pricing Dashboard target-band dataset never had a
|
|
113
|
+
`backgroundColor` (pre-existing authoring slip) so the band shades rather than tints; the
|
|
114
|
+
three chart pages cannot be statically proven light-identical and still need a browser pass.
|
|
100
115
|
|
|
101
116
|
## Security
|
|
102
117
|
- `config.*.ini` carries committed plaintext secrets (team-accepted, see architecture Known
|
|
@@ -104,5 +119,10 @@ about cost; cost is driven by tool-call **intensity** via the profile
|
|
|
104
119
|
(`[api] talos_backend_url` / `talos_backend_key`); no values recorded. Do not add secrets.
|
|
105
120
|
|
|
106
121
|
## Change history
|
|
122
|
+
- 2026-08-04 — Tokenised these pages for the new app-wide light/dark theme: page-local styles
|
|
123
|
+
now use `--c-*` tokens (Talos pages keep their own `--c-bs-*` Bootstrap ramp), and the three
|
|
124
|
+
Chart.js pages read colours via `TogaTheme.color()` + repaint on `TogaTheme.onChange()` with
|
|
125
|
+
`chart.update()`. Recorded the dark-mode open items (error-toast contrast, missing
|
|
126
|
+
target-band `backgroundColor`). Not committed. (jcardinal)
|
|
107
127
|
- 2026-08-04 — Restructured to **Contracts / Pricing Dashboard / Usage / Pricing Settings**. New Contracts pages replace Onboarding with a real PROSPECT/ACTIVE/CHURNED lifecycle (signed contracts churn, never hard-delete — `TalosPricingBands` cascades; post-signature edits append to `TalosContractAmendments`), plus actual-vs-estimate variance, month history chart and one-click apply-recommended-org-fee. Settings replaces the Cost Factors editor with measured rows read-only (`UPDATE ... AND isDerived = 0`). Benchmarks page deleted. Pricing Dashboard rebuilt for sales leadership. Fixed `usage.php clientOptions()` reading an always-empty `TalosClients` (selector rendered empty) → `GET /api/usage/clients` with Team-DB fallback; added cost-intensity panel. Estimator moved to token unit price + workload profiles. Wrote the missing `.app-content:has(.tool--wide)` rule and consolidated Talos CSS into `style.css`. (jcardinal)
|
|
108
128
|
- 2026-06-29 — Built the Talos Pricing UI: nav folder + 4 pages (Onboarding w/ live JS estimate and contract-signing that locks the band schedule into `TalosClients`+`TalosPricingBands` in one txn; read-only Pricing Dashboard + Usage Benchmarks; technical-only Cost Factors editor) and `App_Talos_Estimator` (reads `TalosCostFactors`, derives conversations/user so sales need not enter it). Reads Team DB via new `db_team` connection. Offerings CHAT/VOICE_TO_VOICE/NATURAL_VOICE = 1.0/0.25/0.50 (voice ambiguous, deferred). (jcardinal)
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: App-Wide Colour Theme (light / dark / auto)
|
|
3
|
+
framework: "1.0"
|
|
4
|
+
repo: tools
|
|
5
|
+
project: Tools
|
|
6
|
+
client: shared
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-08-04
|
|
10
|
+
owners: [jcardinal]
|
|
11
|
+
files:
|
|
12
|
+
- tools/assets/css/theme.css
|
|
13
|
+
- tools/assets/js/theme.js
|
|
14
|
+
- tools/_/app/frameworkindex.php
|
|
15
|
+
- tools/_/app/nav.php
|
|
16
|
+
- tools/common/header.php
|
|
17
|
+
- tools/mvc/login/get.php
|
|
18
|
+
- tools/assets/css/style.css
|
|
19
|
+
- tools/assets/css/design.css
|
|
20
|
+
- tools/assets/css/login.css
|
|
21
|
+
- tools/assets/css/cloudfront-clients.css
|
|
22
|
+
- tools/assets/css/gchart.css
|
|
23
|
+
- tools/mvc/talos/usage/get.php
|
|
24
|
+
- tools/mvc/talos/pricing/get.php
|
|
25
|
+
- tools/mvc/talos/contracts/get.php
|
|
26
|
+
- tools/mvc/clickup/dashboard/get.php
|
|
27
|
+
- tools/mvc/email-migration/notify/post.php
|
|
28
|
+
related:
|
|
29
|
+
- ../architecture.md
|
|
30
|
+
- ./persona-gated-navigation.md
|
|
31
|
+
- ./talos-pricing-ui.md
|
|
32
|
+
- ./clickup-sprint-dashboard.md
|
|
33
|
+
- ./legacy-email-notifier.md
|
|
34
|
+
- ./design-demo-admin.md
|
|
35
|
+
- ../../../standards/frontend.md
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Summary
|
|
39
|
+
|
|
40
|
+
A **light / dark / auto** colour theme for the *entire* Tools app, built as a single
|
|
41
|
+
semantic-token layer (`assets/css/theme.css`) rather than per-page edits. The user's choice
|
|
42
|
+
is a three-way switcher rendered next to their name in the left-nav footer, persisted in
|
|
43
|
+
**`localStorage` (`toga-tools-theme`) — deliberately not a DB column**, and applied
|
|
44
|
+
**before first paint** by an inline bootstrap script so there is no flash of the wrong theme.
|
|
45
|
+
|
|
46
|
+
**Light mode is a hard constraint: pixel-identical to before the change**, verified
|
|
47
|
+
mechanically (see [Verification](#verification-how-to-prove-light-mode-did-not-change)).
|
|
48
|
+
Any new page or stylesheet in this app should consume `--c-*` tokens and will then theme for
|
|
49
|
+
free.
|
|
50
|
+
|
|
51
|
+
> **Status (2026-08-04): NOT COMMITTED.** All of this lives in the working tree of the local
|
|
52
|
+
> `tools` checkout, which is on `_production` — it needs a feature branch before it lands.
|
|
53
|
+
> Verified **statically only**: resolver diffs, contrast maths, `php -l` on the 14 changed PHP
|
|
54
|
+
> files, `node --check` on `theme.js`. **No browser check and no specialist-reviewer pass has
|
|
55
|
+
> been run.** See [Open items](#open-items).
|
|
56
|
+
|
|
57
|
+
## How it works
|
|
58
|
+
|
|
59
|
+
### 1. The token layer — `assets/css/theme.css`
|
|
60
|
+
|
|
61
|
+
~142 semantic CSS custom properties are declared on `:root` **with the original light
|
|
62
|
+
values**, and overridden in a `:root[data-theme="dark"]` block. It is registered **first** in
|
|
63
|
+
`App_FrameworkIndex_Tools::__construct()` so every other stylesheet resolves through it.
|
|
64
|
+
|
|
65
|
+
Token groups and their meaning:
|
|
66
|
+
|
|
67
|
+
| Group | Meaning |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| `--c-surface*` | fills |
|
|
70
|
+
| `--c-text*` | foregrounds |
|
|
71
|
+
| `--c-border*` | borders |
|
|
72
|
+
| `--c-primary*` | brand (`-tNN` = alpha tint) |
|
|
73
|
+
| `--c-nav-*` | left nav / public header — **dark in BOTH themes** |
|
|
74
|
+
| `--c-on-accent`, `--c-on-dark-tNN` | white in BOTH themes; content sitting on a permanently dark/coloured fill |
|
|
75
|
+
| `--c-slate-*`, `--c-indigo-*`, `--c-green-*`, `--c-red-*` | secondary ramp used by `design.css` + `cloudfront-clients.css` |
|
|
76
|
+
| `--c-bs-*` | legacy Bootstrap ramp, Talos pages only (see gotchas) |
|
|
77
|
+
| `--c-code-bg`, `--c-code-fg` | fixed dark code slab (not themed) |
|
|
78
|
+
|
|
79
|
+
`color-scheme` is itself tokenised as `--c-color-scheme` so native scrollbars and form
|
|
80
|
+
controls follow the theme. Note `style.css` previously hard-coded `color-scheme: light` on
|
|
81
|
+
`html, body`, which beat the `:root` token — that had to be removed.
|
|
82
|
+
|
|
83
|
+
### 2. Pre-paint application (no flash)
|
|
84
|
+
|
|
85
|
+
`App_FrameworkIndex_Tools::themeBootstrapScript()` returns a tiny inline `<script>` injected
|
|
86
|
+
with `App_FrameworkIndex::setHeaderCode()`; `library` echoes it inside `<head>` **after** the
|
|
87
|
+
CSS/JS includes. It reads `localStorage` and stamps `data-theme` + `data-theme-pref` onto
|
|
88
|
+
`<html>` before the first paint. **An external JS file cannot do this** — it runs too late and
|
|
89
|
+
you get a flash of the wrong theme.
|
|
90
|
+
|
|
91
|
+
### 3. Runtime API — `assets/js/theme.js` (`window.TogaTheme`)
|
|
92
|
+
|
|
93
|
+
- `get()` → `'light' | 'dark'` (resolved theme)
|
|
94
|
+
- `getPreference()` → `'light' | 'dark' | 'system'`
|
|
95
|
+
- `set(pref)` — persists + applies
|
|
96
|
+
- `onChange(fn)` — **fires immediately with the current theme**, then on every change, so a
|
|
97
|
+
caller has one code path for both "colour yourself now" and "recolour"
|
|
98
|
+
- `color('--token')` — read a themed colour out of CSS for JS-driven visuals
|
|
99
|
+
|
|
100
|
+
Persistence: `localStorage` key **`toga-tools-theme`**, values `light|dark|system`. Every
|
|
101
|
+
access is `try`/`catch`ed — Safari private mode *throws* rather than returning `null`.
|
|
102
|
+
`system` follows the OS live via a `matchMedia('(prefers-color-scheme: dark)')` change
|
|
103
|
+
listener (with the `addListener` fallback for Safari <14), and a `storage` event listener keeps
|
|
104
|
+
multiple open tabs in agreement.
|
|
105
|
+
|
|
106
|
+
### 4. The switcher (server-rendered)
|
|
107
|
+
|
|
108
|
+
Rendered in `common/header.php` inside `.app-nav-footer` — next to the signed-in user's name,
|
|
109
|
+
above **Log out** — as a `role="radiogroup"` of three buttons carrying
|
|
110
|
+
`data-theme-choice="light|dark|system"` (labelled **Light / Dark / Auto**). `theme.js` manages
|
|
111
|
+
`aria-checked` + a roving `tabindex` and supports arrow keys. Icons come from a new
|
|
112
|
+
`App_Nav::themeIcon($theme)` that returns **inline SVG** from a fixed 3-key allowlist — inline
|
|
113
|
+
so the glyph inherits `currentColor`.
|
|
114
|
+
|
|
115
|
+
### 5. Login-page wordmark
|
|
116
|
+
|
|
117
|
+
Two `<img>` wordmarks are in the markup and CSS shows one per theme
|
|
118
|
+
(`.login-logo--light` / `.login-logo--dark`, keyed off `:root[data-theme="dark"]`):
|
|
119
|
+
`toga-horizontal-colored.png` in light, `toga-horizontal-white.png` in dark.
|
|
120
|
+
|
|
121
|
+
Chosen over swapping `src` in JS or using `<picture>`: CSS honours an **explicit** Light/Dark
|
|
122
|
+
choice, whereas a `<picture>` media query only sees `prefers-color-scheme` and would ignore a
|
|
123
|
+
forced override — and CSS cannot flash the wrong logo. Both PNGs are 3610×700 so there is no
|
|
124
|
+
reflow. Tradeoff: the browser downloads both (~49 KB extra).
|
|
125
|
+
|
|
126
|
+
### 6. Chart.js pages
|
|
127
|
+
|
|
128
|
+
`talos/usage`, `talos/pricing` and `talos/contracts` read series/axis/tick/gridline colours
|
|
129
|
+
from tokens via `TogaTheme.color()` inside a `paint()` function registered with
|
|
130
|
+
`TogaTheme.onChange(paint)`, followed by `chart.update()`. **`chart.update()` is required** —
|
|
131
|
+
it clears Chart.js's resolver cache so `Chart.defaults` changes take effect on charts that
|
|
132
|
+
already exist. Without this, Chart.js defaults axis/tick text to a dark colour that is
|
|
133
|
+
unreadable on a dark background.
|
|
134
|
+
|
|
135
|
+
## Decisions
|
|
136
|
+
|
|
137
|
+
- **Left nav and public header stay dark in BOTH themes** (deepened for dark). Preserves the
|
|
138
|
+
app's identity, guarantees zero light-mode regression there, and keeps the existing white
|
|
139
|
+
brandmark PNG usable in both themes.
|
|
140
|
+
- **Preference is browser-local (`localStorage`), not a DB column** — explicit developer
|
|
141
|
+
requirement.
|
|
142
|
+
- **Light mode must be pixel-identical**, proven mechanically rather than by eye.
|
|
143
|
+
- **`assets/css/style.scss` left untouched** — neither revived nor deleted (out of scope; that
|
|
144
|
+
is already the status quo — see gotcha below).
|
|
145
|
+
|
|
146
|
+
## Verification (how to prove light mode did not change)
|
|
147
|
+
|
|
148
|
+
1. **Resolver diff.** Resolve every `var(--c-*)` back through the `:root` (light) block and
|
|
149
|
+
diff the result against the pre-change file. Identical output = light mode unchanged.
|
|
150
|
+
2. **Dark-coverage assert.** Every `:root` token must have a `[data-theme="dark"]` override
|
|
151
|
+
unless it is intentionally fixed.
|
|
152
|
+
3. **Per-file colour multiset vs. `git HEAD`.** Compare the *multiset* of resolved colour
|
|
153
|
+
values per file, not a line-by-line diff — an index/line-based diff is useless once lines
|
|
154
|
+
are inserted, whereas a multiset comparison is immune to line-number shifts.
|
|
155
|
+
|
|
156
|
+
## Gotchas
|
|
157
|
+
|
|
158
|
+
- **`assets/css/style.scss` is DEAD in this repo — edit `style.css` directly.** Many recent
|
|
159
|
+
commits touch the `.css` while the `.scss` was last touched in an old ticket; the `.css` has
|
|
160
|
+
drifted ~450 lines ahead. There is no sass binary and no `package.json`. "Helpfully"
|
|
161
|
+
recompiling the SCSS would **silently revert a large amount of shipped work**.
|
|
162
|
+
- **`App_FrameworkIndex::setHeaderCode()` is the only hook for injecting markup into `<head>`
|
|
163
|
+
in 1.0** (library echoes `$this->headerCode` inside `<head>` after the CSS/JS includes). It
|
|
164
|
+
**SETS rather than appends**, and before this feature it was unused anywhere in tools — so a
|
|
165
|
+
**second caller silently clobbers the first**. If another feature needs head markup, refactor
|
|
166
|
+
to compose both strings; do not add a second `setHeaderCode()` call.
|
|
167
|
+
- **A colour literal used as both a foreground and a background must be split into two
|
|
168
|
+
tokens**, or dark mode inverts it the wrong way. Real cases in tools: `#10113A` (body text
|
|
169
|
+
*and* nav/public-header background), `#20242c` (text-strong *and* a dark chip fill), `#999`
|
|
170
|
+
(text grey *and* a badge fill), `#eef0f4` (border *and* badge fill), `#0f172a` (design.css
|
|
171
|
+
`--d-ink` text *and* the code-block background). Detect these **mechanically** — collect, per
|
|
172
|
+
candidate token, the set of CSS properties it appears with (background vs. colour/border).
|
|
173
|
+
Do not eyeball it.
|
|
174
|
+
- **A brand colour needs TWO tokens in a dark theme.** `--c-primary` (text/borders on a panel)
|
|
175
|
+
is **lightened** in dark for legibility; `--c-primary-fill` (a fill behind white text) is
|
|
176
|
+
kept **dark enough** in dark mode to preserve contrast. The roles pull in opposite
|
|
177
|
+
directions — one token makes white-on-brand buttons fail contrast.
|
|
178
|
+
- **The Talos admin pages keep their own `--c-bs-*` group.** They were authored against
|
|
179
|
+
Bootstrap's palette (`#0d6efd`, `#6c757d`, `#495057`, `#dc3545`, `#198754`…), which is close
|
|
180
|
+
to but **not** the TOGA brand palette; folding them into `--c-primary` visibly restyles those
|
|
181
|
+
pages. Only map a literal onto a token whose light value is **identical**.
|
|
182
|
+
- **`mvc/email-migration/notify/post.php` generates EMAIL HTML — its colour literals must stay
|
|
183
|
+
hard-coded.** Email clients cannot resolve CSS custom properties. It is the same file family
|
|
184
|
+
as the on-screen page, so it is easy to tokenise by mistake.
|
|
185
|
+
- **`mvc/clickup/dashboard/get.php` keeps ~6 fixed literals on purpose.** Its card/frame colour
|
|
186
|
+
`#0b1020` matches the embedded Power BI report canvas so nothing flashes white while the
|
|
187
|
+
iframe loads. Do not theme those.
|
|
188
|
+
|
|
189
|
+
## Open items
|
|
190
|
+
|
|
191
|
+
- The three Chart.js pages **cannot** be statically proven light-identical — their colours are
|
|
192
|
+
now generated at runtime. Needs a browser pass.
|
|
193
|
+
- The Talos error toast measures ~2.4:1 in dark (white text on `--c-danger`) and probably wants
|
|
194
|
+
a fixed dark fill.
|
|
195
|
+
- `talos/pricing`'s target-band dataset never had a `backgroundColor` (a pre-existing authoring
|
|
196
|
+
slip), so its band renders as a faint darkening rather than a green tint.
|
|
197
|
+
- A few Talos overlay shadows shifted from black-based to slate-based (imperceptible but real),
|
|
198
|
+
and 7 imperceptible colour substitutions were accepted on Talos pages (largest:
|
|
199
|
+
`#f8f9fa` → `#f8f9fb`).
|
|
200
|
+
|
|
201
|
+
## Change history
|
|
202
|
+
- 2026-08-04 — Built the app-wide light/dark/auto theme: `assets/css/theme.css` (~142 `--c-*`
|
|
203
|
+
tokens on `:root` + a `[data-theme="dark"]` override, registered first in
|
|
204
|
+
`App_FrameworkIndex_Tools`), `assets/js/theme.js` (`window.TogaTheme`), a pre-paint inline
|
|
205
|
+
bootstrap via `setHeaderCode()`, a Light/Dark/Auto radiogroup in the nav footer
|
|
206
|
+
(`App_Nav::themeIcon()`), per-theme login wordmarks, and token-driven Chart.js repaint on the
|
|
207
|
+
three Talos chart pages. Preference in `localStorage` (`toga-tools-theme`), no DB column; nav
|
|
208
|
+
+ public header stay dark in both themes; light mode proven unchanged by resolver diff.
|
|
209
|
+
**Not committed — working tree on `_production`, no browser verification yet.** (jcardinal)
|
|
@@ -5,12 +5,13 @@ project: Library
|
|
|
5
5
|
client: shared
|
|
6
6
|
type: standard
|
|
7
7
|
status: active
|
|
8
|
-
updated: 2026-
|
|
8
|
+
updated: 2026-08-04
|
|
9
9
|
owners: [jcardinal]
|
|
10
10
|
files: []
|
|
11
11
|
related:
|
|
12
12
|
- ./backend-php.md
|
|
13
13
|
- ./framework-rules.md
|
|
14
|
+
- ../apps/tools/features/theme-light-dark.md
|
|
14
15
|
- ../apps/tools/features/design-demo-admin.md
|
|
15
16
|
- ../apps/tools/features/talos-kb-documents-admin.md
|
|
16
17
|
---
|
|
@@ -66,10 +67,48 @@ Each round trip adds a level. Equality comparisons then fail too, because a fres
|
|
|
66
67
|
- `tools/_/app/design/github.php` — `App_Design_Github::decodeEntities()` applied to tab
|
|
67
68
|
names and project titles on both the storage-read and request-read sides.
|
|
68
69
|
|
|
70
|
+
## Tokenising an existing stylesheet for a second theme
|
|
71
|
+
|
|
72
|
+
When retrofitting light/dark onto a legacy sheet, four rules turn a guessing game into a
|
|
73
|
+
mechanical exercise.
|
|
74
|
+
|
|
75
|
+
**1. Split any literal used as both a foreground and a background into two tokens.** A single
|
|
76
|
+
token inverts the wrong way in the second theme. Detect these **mechanically** — collect, per
|
|
77
|
+
candidate token, the set of CSS properties it appears with (`background*` vs.
|
|
78
|
+
`color`/`border*`) — do not eyeball it. Real cases in `tools`: `#10113A` (body text *and* nav
|
|
79
|
+
background), `#20242c`, `#999`, `#eef0f4`, `#0f172a`.
|
|
80
|
+
|
|
81
|
+
**2. A brand colour needs TWO tokens.** `--c-primary` (text/borders on a panel) must be
|
|
82
|
+
**lightened** in dark for legibility; `--c-primary-fill` (a fill behind white text) must stay
|
|
83
|
+
**dark enough** in dark to preserve contrast. The roles pull in opposite directions; one token
|
|
84
|
+
makes white-on-brand buttons fail contrast.
|
|
85
|
+
|
|
86
|
+
**3. Only map a literal onto a token whose light value is IDENTICAL.** Measure perceptual
|
|
87
|
+
distance before accepting a "close enough" substitution — a near-match silently restyles the
|
|
88
|
+
page. Legacy pages authored against a *different* palette (the Talos admin pages use
|
|
89
|
+
Bootstrap's `#0d6efd`/`#6c757d`/`#dc3545`/`#198754`) keep their own token group with their
|
|
90
|
+
exact original values rather than being folded into the brand tokens.
|
|
91
|
+
|
|
92
|
+
**4. Prove the original theme is unchanged, mechanically.** (a) Resolve every `var(--…)` back
|
|
93
|
+
through the `:root` block and diff against the pre-change file; (b) assert every `:root` token
|
|
94
|
+
has a second-theme override unless intentionally fixed; (c) compare the per-file **multiset**
|
|
95
|
+
of resolved colour values against `git HEAD` — a line-index diff is useless once lines are
|
|
96
|
+
inserted, a multiset comparison is immune to line shifts.
|
|
97
|
+
|
|
98
|
+
Two things are **never** tokenised: HTML destined for an **email** (email clients cannot
|
|
99
|
+
resolve custom properties) and colours deliberately matched to third-party embedded content
|
|
100
|
+
(e.g. an iframe's canvas colour, to prevent a white flash on load).
|
|
101
|
+
|
|
102
|
+
Reference implementation:
|
|
103
|
+
[tools App-Wide Colour Theme](../apps/tools/features/theme-light-dark.md).
|
|
104
|
+
|
|
69
105
|
## Change history
|
|
70
106
|
|
|
107
|
+
- 2026-08-04 — Added "Tokenising an existing stylesheet for a second theme": split
|
|
108
|
+
dual-role colour literals, two tokens per brand colour, identical-value-only substitutions,
|
|
109
|
+
and the mechanical proof that the original theme is unchanged; plus the never-tokenise cases
|
|
110
|
+
(email HTML, colours matched to third-party embeds). Drawn from the `tools` light/dark
|
|
111
|
+
build. (jcardinal)
|
|
71
112
|
- 2026-07-30 — Created. Establishes the escape-once/plain-text-storage invariant after a
|
|
72
113
|
second independent occurrence in `tools` (Design Demo Admin, following the Talos
|
|
73
114
|
vocabulary tool). (jcardinal)
|
|
74
|
-
</content>
|
|
75
|
-
</invoke>
|
|
@@ -364,6 +364,18 @@ in `_underscore` and `library` in the **same** release, and the `/errors` consol
|
|
|
364
364
|
`dbchanges2/Logs_Client/2026-08-01a - Drop Error table.sql`. Do not reintroduce it.
|
|
365
365
|
- **Shared-schema migration collision.** Because these tables live in the *shared* Core Logs
|
|
366
366
|
DB, only **one** migration may `CREATE` them; later migrations only `ALTER`.
|
|
367
|
+
- **The `dbchanges2/Logs/` migration files LAG the deployed schema — read the live cluster, not the
|
|
368
|
+
migrations.** `dbchanges2/Logs/2026-07-30a - Error reporting Issues and Events.sql` does **not**
|
|
369
|
+
describe the live `Logs.Issue`: notably the identity column is **`issueKey` (varchar 128)**, not
|
|
370
|
+
`code`. Live columns include `reference`, `dtLastOccurred`, `isManaged`, `urgency`,
|
|
371
|
+
`minimumUrgency`, `consecutiveQuietWindows`, `subject`, `errorMessage`, `occurrences`,
|
|
372
|
+
`totalOccurrences`, `recurrenceCount`, `dtLastNotified`, `notificationCount`, `dtAcknowledged`,
|
|
373
|
+
`dtMutedUntil`, `createsClickupTask`, `clickupPriority`, `clickupAssigneeId`. Query
|
|
374
|
+
`information_schema` (or `SHOW TABLES` / `SHOW COLUMNS`) against the live Logs cluster **before**
|
|
375
|
+
writing any SQL against this feature — reading the migrations instead produced confidently wrong
|
|
376
|
+
conclusions twice on 2026-08-04. Same trap for the **singular** table names
|
|
377
|
+
(`Issue`/`Event`/`IssueFingerprint`/`IssueAreaOwner`/`IssueClickupTask`/`IssueEmailAddress`,
|
|
378
|
+
renamed by `2026-08-01a` and confirmed applied in prod).
|
|
367
379
|
- **Pre-existing committed secrets in this area (not fixed — out of scope, report only).**
|
|
368
380
|
A ClickUp API token at `_underscore/Component/Api/Clickup/Clickup.php:5`; SES SMTP
|
|
369
381
|
credentials at `_underscore/Email.php:11-12`; a named individual's Sentry personal access
|
|
@@ -418,6 +430,12 @@ clientUserId). **Neither was built.** As built instead:
|
|
|
418
430
|
|
|
419
431
|
## Change history
|
|
420
432
|
|
|
433
|
+
- 2026-08-04 — **Correction/diagnostic:** recorded that the `dbchanges2/Logs/` migration files lag
|
|
434
|
+
the deployed schema — `2026-07-30a` describes a `code` identity column, but live `Logs.Issue` is
|
|
435
|
+
keyed on **`issueKey` (varchar 128)** — and listed the live column set. Query
|
|
436
|
+
`information_schema` against the live Logs cluster before writing SQL here; trusting the
|
|
437
|
+
migrations (and forgetting the applied singular rename) produced two confidently wrong
|
|
438
|
+
conclusions in one session. No code change. (dfranks)
|
|
421
439
|
- 2026-08-04 (later, **uncommitted/undeployed** at time of writing) — **Reference encoding reworked
|
|
422
440
|
to base 32** over `'0123456789ACDEFGHJKMNPQRSTUVWXYZ'` (all digits kept; `O`/`I`/`L`/`B` dropped
|
|
423
441
|
as impersonators of `0`/`1`/`1`/`8`), replacing the base-26 decimal-counter+letter scheme, with
|
|
@@ -7,7 +7,7 @@ client: shared
|
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
9
|
updated: 2026-08-04
|
|
10
|
-
owners: ["jcardinal"]
|
|
10
|
+
owners: ["jcardinal", "dfranks"]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Worker/Infrastructure/Errors.php
|
|
13
13
|
- worker2/Worker/Notification/Email.php
|
|
@@ -28,6 +28,7 @@ related:
|
|
|
28
28
|
- ./clickup-project-routing.md
|
|
29
29
|
- ./notification-email.md
|
|
30
30
|
- ./notification-email-template.md
|
|
31
|
+
- ../../../clients/compass-usa/features/mits-sales-order-transmission-alerting.md
|
|
31
32
|
---
|
|
32
33
|
|
|
33
34
|
## Summary
|
|
@@ -263,6 +264,22 @@ this controller needs the same treatment.**
|
|
|
263
264
|
issue gets a **neutral** subject rather than another client's raw error text.
|
|
264
265
|
- **`IssueEmailAddress.clientId = 0` means "all clients"** and requires an explicit
|
|
265
266
|
confirmation flag in the Tools console; list views render it as **"ALL CLIENTS"**.
|
|
267
|
+
- **A recipient scoped to a specific `clientId` delivers NOTHING for occurrences recorded without
|
|
268
|
+
client attribution.** `loadRecentClientIds()` maps an occurrence whose `clientId` is NULL to
|
|
269
|
+
**`0`**, and `resolveRecipientsForClient()` resolves scope `0` **plus** the erroring client. So
|
|
270
|
+
the rule is: scope to **`0`** when the `issueKey` is raised from a **single client-specific code
|
|
271
|
+
path** (a Compass-only cron, say); **never** scope to `0` for an `issueKey` raised from shared
|
|
272
|
+
code, which would leak one client's alerts to another's recipients.
|
|
273
|
+
- **Configuring a recipient is never retroactive.** `shouldNotify()` notifies on first occurrence
|
|
274
|
+
(`notificationCount` 0), on escalation, or when the backed-off reminder interval has elapsed —
|
|
275
|
+
and returns **false when the current window has zero events**. The first email therefore lands on
|
|
276
|
+
the **next live occurrence**, not on the backlog. Do not read silence right after a config change
|
|
277
|
+
as a failure.
|
|
278
|
+
- **Adding a business recipient is purely additive — it cannot switch ClickUp ticketing off.**
|
|
279
|
+
Routing is **both**, not either: `if ($recipients !== null)` emails the business recipients and
|
|
280
|
+
`if ($issue->createsClickupTask)` tickets the dev team, evaluated **independently**. The in-code
|
|
281
|
+
comment marking this records the earlier `elseif` form, where adding one recipient silently
|
|
282
|
+
disabled an issue's dev-team ticket.
|
|
266
283
|
- **The ClickUp webhook has no HMAC signature verification** (pre-existing, unfixed). A forged
|
|
267
284
|
POST can mark issues acknowledged — freezing the neglect axis — or resolve episodes. That is
|
|
268
285
|
alert suppression against the error-reporting system itself. Fix before relying on this
|
|
@@ -273,6 +290,15 @@ this controller needs the same treatment.**
|
|
|
273
290
|
|
|
274
291
|
## Change history
|
|
275
292
|
|
|
293
|
+
- 2026-08-04 (verified against `origin/_production` + the prod Logs cluster) — Recorded the
|
|
294
|
+
**recipient-routing semantics** a developer needs before configuring a business alert: routing is
|
|
295
|
+
**both** business email and dev-team ClickUp, evaluated independently (adding a recipient is
|
|
296
|
+
purely additive); `IssueEmailAddress.clientId` scoping means a client-scoped row delivers nothing
|
|
297
|
+
for occurrences with NULL `clientId` (mapped to `0`), so single-client `issueKey`s scope to `0`
|
|
298
|
+
and shared-code `issueKey`s never do; and `shouldNotify()` makes configuration
|
|
299
|
+
**non-retroactive** — the first email lands on the next live occurrence. Also noted that prod
|
|
300
|
+
`Logs.IssueEmailAddress` was still empty, making Compass's MITS rejection the platform's first
|
|
301
|
+
business-routed issue. No code change. (dfranks)
|
|
276
302
|
- 2026-08-04 (later, **uncommitted/undeployed** at time of writing) — **Fixed: escalation email had
|
|
277
303
|
never been sent, ever**, for two independent reasons. (A) `processTechnicalIssue()` returned early
|
|
278
304
|
when the ClickUp task would read identically, and that return sat *above* the `if ($shouldNotify)`
|
package/knowledge/INDEX.md
CHANGED
|
@@ -14,7 +14,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
14
14
|
- **walmarttechservices** (Walmart Tech Services) — 1 doc(s) → [1.0/apps/walmarttechservices/INDEX.md](1.0/apps/walmarttechservices/INDEX.md)
|
|
15
15
|
- **test** (Test) — 14 doc(s) → [1.0/apps/test/INDEX.md](1.0/apps/test/INDEX.md)
|
|
16
16
|
- **toga** (TOGa) — 2 doc(s) → [1.0/apps/toga/INDEX.md](1.0/apps/toga/INDEX.md)
|
|
17
|
-
- **tools** (Tools) —
|
|
17
|
+
- **tools** (Tools) — 14 doc(s) → [1.0/apps/tools/INDEX.md](1.0/apps/tools/INDEX.md)
|
|
18
18
|
|
|
19
19
|
## 2.0 framework
|
|
20
20
|
|
|
@@ -39,7 +39,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
39
39
|
- **togatech** (TOGA Technology Website) — 4 doc(s) → [standalone/apps/togatech/INDEX.md](standalone/apps/togatech/INDEX.md)
|
|
40
40
|
- **websocket** (WebSocket Server) — 2 doc(s) → [standalone/apps/websocket/INDEX.md](standalone/apps/websocket/INDEX.md)
|
|
41
41
|
- **forward** (Forwarder) — 3 doc(s) → [standalone/apps/forward/INDEX.md](standalone/apps/forward/INDEX.md)
|
|
42
|
-
- **claude** (Claude Harness) —
|
|
42
|
+
- **claude** (Claude Harness) — 4 doc(s) → [standalone/apps/claude/INDEX.md](standalone/apps/claude/INDEX.md)
|
|
43
43
|
|
|
44
44
|
## Clients
|
|
45
45
|
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
| [Compass: Item-Fulfillment TableViews (for-sales-order-items & for-sales-orders, tracking via bridge)](features/item-fulfillment-tracking-tableview.md) | 2.0 | Two sibling Compass TableViews in `Client_Compass` display fulfilled items in toga2-supply, both driven by `TableViews` / `TableViewJoins` / `TableViewFields` c | dbchanges2/Client_Compass/2026-06-10 - ItemFulfillmentsForSalesOrderItemsTableView.sql, dbchanges2/Client_Compass/2026-06-11 - ItemFulfillmentsForSalesOrdersTableView.sql, dbchanges2/Client_Compass/2026-06-15a - FixItemFulfillmentTrackingNumberJoins.sql, dbchanges2/Client/2026-07-15a - ExcludeFeeItemsFromItemFulfillmentsForSalesOrdersView.sql |
|
|
9
9
|
| [Compass MITS PO → SO Item Linking](features/mits-po-to-so-item-linking.md) | 2.0 | MITS sends Compass inbound Purchase Orders (`POST /v2/purchase-orders`) against a Sales Order (`mitsSalesOrder`). | _underscore/Model/Compass/PurchaseOrder.php, worker/crons/toga2/compass/workflow/3a_import_office_depot_purchase_orders.php |
|
|
10
10
|
| [Compass MITS PO Transmission to Vendors](features/mits-po-transmission-to-vendors.md) | 2.0 | The 1.0 worker cron `2_transmit_mits_purchase_orders_to_vendors.php` transmits Compass PurchaseOrders to their vendors (Office Depot, Strategic Systems, Compass | worker/crons/toga2/compass/workflow/2_transmit_mits_purchase_orders_to_vendors.php, worker/crons/toga2/compasscanada/workflow/2_transmit_mits_purchase_orders_to_vendors.php, library/app/client/compass.php |
|
|
11
|
+
| [Compass MITS Sales-Order Transmission — Rejection Alerting (Issue/Event, not email-in-cron)](features/mits-sales-order-transmission-alerting.md) | 2.0 | When MITS **rejects** a Compass sales order transmitted by the 1.0 cron `1_transmit_compass_sales_orders_to_mits.php`, the alert is no longer an email built ins | worker/crons/toga2/compass/workflow/1_transmit_compass_sales_orders_to_mits.php, worker2/Worker/Infrastructure/Errors.php, _underscore/Model/Core/Logs/Issue.php, _underscore/Model/Core/Logs/IssueEmailAddress.php, dbchanges2/Logs/2026-08-04a - MITS rejection business recipients.sql |
|
|
11
12
|
| [Compass MR/MA Order Auto-Approval & Status Gate](features/mr-ma-order-approval-and-status.md) | 2.0 | Compass **MR** and **MA** sales orders are system-generated from the MITS / Office Depot EDI pipeline (they do not originate as user-entered SA orders) and must | _underscore/Model/Compass/SalesOrder.php, _underscore/Model/Compass/PurchaseOrder.php, worker/crons/toga2/compass/workflow/3a_import_office_depot_purchase_orders.php |
|
|
12
13
|
| [Compass USA](profile.md) | 2.0 | Compass USA is a TOGA client running a multi-tier supply-chain commerce operation. | |
|
|
13
14
|
| [Compass Cross-Kit Bundle Corruption — Detection & Repair](workflows/cross-kit-bundle-corruption.md) | 2.0 | A frontend regression in `toga2-commerce`'s edit-order bundle submission mis-attributed bundle (kit) line items and **fees/warranties** to the **wrong kit**, pe | src/api/syncSalesOrderItemsFromLocalStorageCartToApi.ts |
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Compass MITS Sales-Order Transmission — Rejection Alerting (Issue/Event, not email-in-cron)
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
project: _Underscore
|
|
5
|
+
client: compass-usa
|
|
6
|
+
type: client-feature
|
|
7
|
+
status: active
|
|
8
|
+
updated: 2026-08-04
|
|
9
|
+
owners: ["dfranks"]
|
|
10
|
+
files:
|
|
11
|
+
- worker/crons/toga2/compass/workflow/1_transmit_compass_sales_orders_to_mits.php
|
|
12
|
+
- worker2/Worker/Infrastructure/Errors.php
|
|
13
|
+
- _underscore/Model/Core/Logs/Issue.php
|
|
14
|
+
- _underscore/Model/Core/Logs/IssueEmailAddress.php
|
|
15
|
+
- dbchanges2/Logs/2026-08-04a - MITS rejection business recipients.sql
|
|
16
|
+
related:
|
|
17
|
+
- mits-po-transmission-to-vendors.md
|
|
18
|
+
- ../../../2.0/apps/worker2/features/error-escalation-cron.md
|
|
19
|
+
- ../../../2.0/apps/_underscore/features/error-reporting-issue-event.md
|
|
20
|
+
- ../../../1.0/apps/library/features/error-capture-1-0.md
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Summary
|
|
24
|
+
|
|
25
|
+
When MITS **rejects** a Compass sales order transmitted by the 1.0 cron
|
|
26
|
+
`1_transmit_compass_sales_orders_to_mits.php`, the alert is no longer an email built inside the
|
|
27
|
+
cron. It is a **business exception** raised into the shared Logs Issue/Event pipeline, and its
|
|
28
|
+
recipients are **rows in `Logs.IssueEmailAddress`**.
|
|
29
|
+
|
|
30
|
+
**Consequence for anyone handed a ticket like "add <person> to the MITS error emails": there is
|
|
31
|
+
no PHP to change.** It is a one-row data change delivered as a `dbchanges2/Logs/` migration.
|
|
32
|
+
|
|
33
|
+
## How it works
|
|
34
|
+
|
|
35
|
+
- The cron raises
|
|
36
|
+
`throw new App_Exception_Business('COMPASS_SALES_ORDER_MITS_TRANSMIT_REJECTED', …, URGENCY_HIGH)`
|
|
37
|
+
on a MITS rejection. (Before 2026-08-03 this block was a hardcoded `new App_Email_Agilant()`
|
|
38
|
+
with `addTo('devteam@togatech.com')`.)
|
|
39
|
+
- Capture creates/dedupes a `Logs.Issue` keyed on the `issueKey`, and the worker2
|
|
40
|
+
`Infrastructure/Errors/Escalate` cron does all outbound notification — business email to the
|
|
41
|
+
configured recipients **and**, independently, the dev-team ClickUp task. See
|
|
42
|
+
[error-escalation-cron](../../../2.0/apps/worker2/features/error-escalation-cron.md).
|
|
43
|
+
- Recipients live in `Logs.IssueEmailAddress` (FK `issueId` → `Logs.Issue`), scoped by
|
|
44
|
+
`clientId` (`0` = all clients).
|
|
45
|
+
|
|
46
|
+
### Adding a recipient (the correct pattern)
|
|
47
|
+
|
|
48
|
+
Write a `dbchanges2/Logs/YYYY-MM-DD<letter>` migration that:
|
|
49
|
+
|
|
50
|
+
1. **Looks the issue up by `issueKey`**, never by its `id`. The id is assigned on the issue's
|
|
51
|
+
**first occurrence** and is therefore not stable across environments.
|
|
52
|
+
2. Uses **`clientId = 0`**. This `issueKey` is raised from a Compass-only code path, and an
|
|
53
|
+
occurrence recorded without client attribution resolves against scope `0` only — a row scoped
|
|
54
|
+
to the Compass `clientId` can deliver nothing. (Never use `0` for an `issueKey` raised from
|
|
55
|
+
shared code — that is a cross-tenant leak.)
|
|
56
|
+
3. Is **idempotent** via `INSERT … WHERE NOT EXISTS`, mirroring the unique key
|
|
57
|
+
`uq_issue_client_address_type`.
|
|
58
|
+
|
|
59
|
+
Configuration is **never retroactive**: `shouldNotify()` fires on the first occurrence, on
|
|
60
|
+
escalation, or when the backed-off reminder interval elapses, and returns false for a window with
|
|
61
|
+
zero events. The first email lands on the **next live rejection**, not on existing history.
|
|
62
|
+
|
|
63
|
+
## Gotchas / known issues
|
|
64
|
+
|
|
65
|
+
- **Do not "modernize" this alert back into the cron.** Migrating it to
|
|
66
|
+
`_Model_Client_EmailTemplate::send` (or any in-cron email) reverts the move to the Issue/Event
|
|
67
|
+
pipeline and removes the recipient configuration surface entirely.
|
|
68
|
+
- **Never re-introduce the payload dump into the alert.** The original email included the
|
|
69
|
+
transmission payload, which carries ship-to addressee, attention line, street address and phone
|
|
70
|
+
number. The Issue message is stored in a table **shared across every client**, displayed to
|
|
71
|
+
internal staff in the Tools `/errors` console, pushed to ClickUp, and emailed — so the payload
|
|
72
|
+
was deliberately removed. Alerts here name the order, not the person.
|
|
73
|
+
- **The MITS cron holds its API credentials inline in the file.** Treat that file as
|
|
74
|
+
secret-bearing when copying snippets anywhere (tickets, PR bodies, knowledge docs).
|
|
75
|
+
- **Adding a recipient does not turn ClickUp ticketing off.** Routing is both-and, not
|
|
76
|
+
either-or — see the escalation-cron doc for the `elseif` bug this used to be.
|
|
77
|
+
- As of 2026-08-04 prod `Logs.IssueEmailAddress` is empty, so this is the **first business-routed
|
|
78
|
+
issue on the platform**: `processBusinessIssue()` will execute in production for the first time
|
|
79
|
+
when this issue next fires. Watch that run rather than assuming it works.
|
|
80
|
+
|
|
81
|
+
## Change history
|
|
82
|
+
- 2026-08-04 — Documented that MITS rejection alerting moved (worker `5521854b`, 2026-08-03) from a
|
|
83
|
+
hardcoded in-cron `App_Email_Agilant` to `App_Exception_Business` +
|
|
84
|
+
`Logs.IssueEmailAddress` recipients, so adding an alert recipient is a data change, not a code
|
|
85
|
+
change. Recorded the deliberate removal of the PII-bearing payload dump from the alert, the
|
|
86
|
+
look-up-by-`issueKey` / `clientId = 0` / `NOT EXISTS` migration pattern, and that recipient
|
|
87
|
+
configuration only takes effect on the next live occurrence. (dfranks)
|
|
88
|
+
</content>
|
|
89
|
+
</invoke>
|
|
@@ -16,10 +16,11 @@ project: _Underscore
|
|
|
16
16
|
client: compass-usa
|
|
17
17
|
type: profile
|
|
18
18
|
status: active
|
|
19
|
-
updated: 2026-
|
|
20
|
-
owners: [jcardinal, bala, tcox, apeterson]
|
|
19
|
+
updated: 2026-08-04
|
|
20
|
+
owners: [jcardinal, bala, tcox, apeterson, dfranks]
|
|
21
21
|
files: []
|
|
22
22
|
related:
|
|
23
|
+
- features/mits-sales-order-transmission-alerting.md
|
|
23
24
|
- features/asn-to-item-fulfillment.md
|
|
24
25
|
- features/cost-centers.md
|
|
25
26
|
- features/approval-decision-flow.md
|
|
@@ -75,6 +76,10 @@ separate, related client (see its own profile).
|
|
|
75
76
|
`worker/crons/toga2/compass/workflow/3b_import_strategic_systems_advance_shipping_notices.php`.
|
|
76
77
|
|
|
77
78
|
## Key features (this client)
|
|
79
|
+
- [MITS Sales-Order Transmission — Rejection Alerting](features/mits-sales-order-transmission-alerting.md)
|
|
80
|
+
— MITS rejection alerts are `App_Exception_Business` Issues with recipients in
|
|
81
|
+
`Logs.IssueEmailAddress`, **not** an email in the cron: adding an alert recipient is a
|
|
82
|
+
`dbchanges2/Logs/` data change with no PHP.
|
|
78
83
|
- [Compass ASN → ItemFulfillment Auto-Creation](features/asn-to-item-fulfillment.md) — the
|
|
79
84
|
`_Model_Compass_AdvanceShippingNotice::postPost` handler.
|
|
80
85
|
- [Cost Centers (Unit Locations, numeric-only)](features/cost-centers.md) — what a Compass
|
|
@@ -5,3 +5,4 @@
|
|
|
5
5
|
| [Harness Distribution — How a knowledge.js Fix Reaches Teammates](workflows/harness-distribution.md) | There are **two copies** of `knowledge.js` on a developer's machine, and the skills use only one of them. | claude/scripts/install.js, claude/knowledge.js |
|
|
6
6
|
| [Knowledge Base Publish / Push Pipeline](workflows/knowledge-publish-pipeline.md) | How `/capture` and `/session-save` get knowledge docs from your working tree into `agilantsolutions/claude` on `_main`. | claude/knowledge.js |
|
|
7
7
|
| [MCP Tool Usage — "Talos" Means the Internal Knowledge Base, and Run Your Own SELECTs](workflows/mcp-tool-usage.md) | Two MCP connections are available in every session, and **Claude is expected to reach for them on its own judgement — nobody should have to say the word "MCP".* | claude/.claude/skills/kickoff/SKILL.md, claude/.claude/skills/capture/SKILL.md |
|
|
8
|
+
| [Ticket-Branch Creation Safety — How /work-ticket Armed Pushes to Production Branches](workflows/ticket-branch-creation-safety.md) | For roughly a month, `/work-ticket` created ticket branches whose **git upstream was the production branch**. | claude/skills/work-ticket/SKILL.md |
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Ticket-Branch Creation Safety — How /work-ticket Armed Pushes to Production Branches
|
|
3
|
+
framework: "standalone"
|
|
4
|
+
repo: claude
|
|
5
|
+
project: Claude Harness
|
|
6
|
+
client: shared
|
|
7
|
+
type: workflow
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-08-04
|
|
10
|
+
owners: ["dfranks"]
|
|
11
|
+
files:
|
|
12
|
+
- claude/skills/work-ticket/SKILL.md
|
|
13
|
+
related:
|
|
14
|
+
- ./harness-distribution.md
|
|
15
|
+
- ../../../2.0/apps/worker2/workflows/ticket-to-pseudocode-planning.md
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Summary
|
|
19
|
+
|
|
20
|
+
For roughly a month, `/work-ticket` created ticket branches whose **git upstream was the
|
|
21
|
+
production branch**. Claude's own pushes were unaffected, but a developer using VS Code's
|
|
22
|
+
**Commit + Sync** mid-session pushed ticket code straight to `_main`/`_production` — no PR, no
|
|
23
|
+
review. Two such pushes landed in two days before it was found.
|
|
24
|
+
|
|
25
|
+
The mechanism is generic git behavior, so it is worth understanding beyond this one skill.
|
|
26
|
+
|
|
27
|
+
## Root cause
|
|
28
|
+
|
|
29
|
+
Branch creation was `git checkout -b TRUE-XXXX "origin/$DEFAULT"`. Git's
|
|
30
|
+
**`branch.autoSetupMerge` defaults to `true`**, so branching from a *remote-tracking ref* adopts
|
|
31
|
+
that ref as the new branch's **upstream**. The ticket branch therefore tracked
|
|
32
|
+
`origin/_main` / `origin/_production` until the skill's later `git push -u` re-pointed it.
|
|
33
|
+
|
|
34
|
+
**Trigger asymmetry is why it hid.** Claude pushes with `git push -u origin <TICKET>` — an
|
|
35
|
+
explicit refspec, which ignores the upstream and was always safe. VS Code's Commit + Sync resolves
|
|
36
|
+
the destination **from the upstream**, so any developer commit made before the skill's first push
|
|
37
|
+
went to the production branch.
|
|
38
|
+
|
|
39
|
+
**A safety fix created the exposure.** Commit `6d155f7` (2026-07-09) moved branch creation from
|
|
40
|
+
*after* implementation to *before* any code is written — itself a genuine fix, because a stale
|
|
41
|
+
local `_production` 12 commits behind nearly reverted upstream work. But it turned a seconds-long
|
|
42
|
+
armed window into the **entire implementation phase**. Correctness fixes can lengthen an existing
|
|
43
|
+
window; check what else depends on the timing you moved.
|
|
44
|
+
|
|
45
|
+
## The fixes
|
|
46
|
+
|
|
47
|
+
1. **Skill:** branch creation uses **`--no-track`** *and* publishes the empty branch immediately
|
|
48
|
+
(`git push -u origin TRUE-XXXX`), so the branch has a correct upstream before any code exists.
|
|
49
|
+
The later push is a plain `git push origin <TICKET>`.
|
|
50
|
+
2. **Global git config:** `branch.autoSetupMerge=simple` (a checkout can no longer adopt a
|
|
51
|
+
production upstream, even outside the skill) plus `push.autoSetupRemote=true`.
|
|
52
|
+
3. **`pre-push` hook** in each app repo rejecting
|
|
53
|
+
`refs/heads/(_main|_production|_beta|_alpha|master)`, override `ALLOW_PROD_PUSH=1`. This also
|
|
54
|
+
catches Claude, because Claude shells out to real `git`.
|
|
55
|
+
4. **Deny rules** in `.claude/settings.json`.
|
|
56
|
+
5. Existing branches still tracking a production branch were **disarmed** individually.
|
|
57
|
+
|
|
58
|
+
`rework-ticket` was never affected — it checks out an existing remote branch.
|
|
59
|
+
|
|
60
|
+
## Recovery: getting an unreviewed commit off an unprotected default branch
|
|
61
|
+
|
|
62
|
+
Do **not** force-push. Instead:
|
|
63
|
+
|
|
64
|
+
1. **Revert via PR** on the default branch.
|
|
65
|
+
2. **Rebuild the ticket branch as a NEW commit** on the post-revert base.
|
|
66
|
+
|
|
67
|
+
> **The revert-remerge trap:** if you keep the original commit and merge that branch later, the
|
|
68
|
+
> original commit is already an **ancestor** of the default branch, so the merge is a silent no-op
|
|
69
|
+
> and the change **never comes back**. The revert wins and nothing warns you.
|
|
70
|
+
|
|
71
|
+
## Gotchas / known issues
|
|
72
|
+
|
|
73
|
+
- **Everything installed here is local and does not survive a fresh clone.** Hooks and git config
|
|
74
|
+
are per-machine; a teammate or a new laptop is unprotected until they redo it.
|
|
75
|
+
- **Standing exposure (not fixed):** `dbchanges2/_main`, `worker/_production`,
|
|
76
|
+
`worker2/_production` and `api2/_production` have **no branch protection and no rulesets**.
|
|
77
|
+
Server-side protection is the only durable fix and needs an admin GitHub account.
|
|
78
|
+
- **Diagnosing this class of incident:** `git reflog` on the remote-tracking branch shows
|
|
79
|
+
`update by push` entries — that is how both incidents were confirmed and how the other four
|
|
80
|
+
repos were cleared. Pre-existing `_beta` pushes are normal manual practice; check dates before
|
|
81
|
+
calling something an incident.
|
|
82
|
+
- Auditing "did this ever happen elsewhere?" requires sweeping **every** repo the skill touches,
|
|
83
|
+
not just the one that surfaced the bug.
|
|
84
|
+
|
|
85
|
+
## Change history
|
|
86
|
+
- 2026-08-04 — Documented the `branch.autoSetupMerge` root cause, the Claude-vs-VS-Code trigger
|
|
87
|
+
asymmetry that hid it for a month, and the fixes (`--no-track` + immediate branch publish,
|
|
88
|
+
global `autoSetupMerge=simple`, per-repo `pre-push` hook, settings deny rules). Recorded the
|
|
89
|
+
revert-then-rebuild recovery pattern and its revert-remerge no-op trap, plus the standing lack
|
|
90
|
+
of server-side branch protection on four app repos. (dfranks)
|
|
91
|
+
</content>
|
package/package.json
CHANGED