@micha.bigler/ui-core-micha 3.14.4 → 4.0.0
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/CHANGELOG.md +25 -0
- package/README.md +7 -0
- package/dist/chat/kitSxRegistry.js +9 -0
- package/dist/components/charts/kitSxRegistry.js +6 -0
- package/dist/entries/charts.js +6 -0
- package/dist/entries/chat.js +8 -0
- package/dist/entries/markdown.js +1 -0
- package/dist/index.js +0 -14
- package/dist/pages/AccountPage.js +1 -2
- package/dist/pages/LoginPage.js +1 -2
- package/dist/pages/PasswordChangePage.js +1 -2
- package/dist/pages/PasswordInvitePage.js +1 -2
- package/dist/pages/PasswordResetRequestPage.js +1 -2
- package/dist/pages/SignUpPage.js +1 -2
- package/dist/pages/SignupConfirmPage.js +1 -2
- package/dist/theme/kitSxEntry.js +11 -0
- package/dist/theme/kitSxRegistry.js +2 -15
- package/package.json +32 -7
- package/.github/workflows/ci.yml +0 -35
- package/.github/workflows/publish.yml +0 -126
- package/DESIGN.md +0 -60
- package/WORK_ORDERS.md +0 -150
- package/dev/entries.jsx +0 -721
- package/dev/fixtures.js +0 -26
- package/dev/main.jsx +0 -75
- package/dev/mockTransport.jsx +0 -128
- package/docs/CHART-LAYOUT.md +0 -157
- package/docs/DARK-TOKENS.md +0 -140
- package/docs/TYPE-SCALE.md +0 -111
- package/docs/messaging-deviations.md +0 -118
- package/index.html +0 -12
- package/renovate.json +0 -10
- package/scripts/chart-api-census.mjs +0 -415
- package/src/auth/AuthContext.jsx +0 -158
- package/src/auth/ReauthModal.jsx +0 -82
- package/src/auth/UserMenu.jsx +0 -121
- package/src/auth/apiClient.jsx +0 -232
- package/src/auth/authApi.jsx +0 -539
- package/src/auth/authConfig.jsx +0 -34
- package/src/auth/reauth.js +0 -47
- package/src/chat/ChatComposer.jsx +0 -36
- package/src/chat/ChatList.jsx +0 -87
- package/src/chat/ChatThread.jsx +0 -128
- package/src/chat/ChatWorkspace.jsx +0 -244
- package/src/chat/MarkdownReply.jsx +0 -22
- package/src/chat/ModelPicker.jsx +0 -85
- package/src/chat/api.js +0 -18
- package/src/chat/useChatSocket.js +0 -32
- package/src/chat/wire.js +0 -7
- package/src/components/AccessCodeManager.jsx +0 -288
- package/src/components/AccessCodeSingleUseToggle.jsx +0 -92
- package/src/components/AllowedEmailDomainsManager.jsx +0 -92
- package/src/components/AuthFactorRequirementCard.jsx +0 -95
- package/src/components/BulkInviteCsvTab.jsx +0 -198
- package/src/components/EmptyState.jsx +0 -38
- package/src/components/LoginForm.jsx +0 -159
- package/src/components/MFAComponent.jsx +0 -350
- package/src/components/MfaLoginComponent.jsx +0 -252
- package/src/components/MobileBottomNav.jsx +0 -82
- package/src/components/PasskeysComponent.jsx +0 -252
- package/src/components/PasswordChangeForm.jsx +0 -87
- package/src/components/PasswordResetRequestForm.jsx +0 -43
- package/src/components/PasswordSetForm.jsx +0 -97
- package/src/components/ProfileComponent.jsx +0 -348
- package/src/components/QrSignupManager.jsx +0 -404
- package/src/components/QrSignupValidityManager.jsx +0 -106
- package/src/components/RegistrationMethodsManager.jsx +0 -150
- package/src/components/SecurityComponent.jsx +0 -141
- package/src/components/SocialLoginButtons.jsx +0 -83
- package/src/components/SoftChip.jsx +0 -93
- package/src/components/StatTile.jsx +0 -39
- package/src/components/SupportRecoveryRequestsTab.jsx +0 -482
- package/src/components/TurnstileWidget.jsx +0 -99
- package/src/components/UserInviteComponent.jsx +0 -76
- package/src/components/UserListComponent.jsx +0 -618
- package/src/components/charts/BarChart.jsx +0 -102
- package/src/components/charts/ChartFrame.jsx +0 -214
- package/src/components/charts/LineChart.jsx +0 -126
- package/src/components/charts/ScatterChart.jsx +0 -299
- package/src/components/charts/TimeSeriesChart.jsx +0 -300
- package/src/components/charts/chartDefaults.js +0 -644
- package/src/components/charts/chartLabels.js +0 -36
- package/src/components/charts/exportChart.js +0 -258
- package/src/components/charts/formatters.js +0 -39
- package/src/components/charts/palette.js +0 -33
- package/src/components/charts/yearTickInterval.js +0 -7
- package/src/i18n/authTranslations.ts +0 -2651
- package/src/i18n/chartsTranslations.ts +0 -91
- package/src/i18n/chatTranslations.ts +0 -55
- package/src/i18n/messagingTranslations.ts +0 -120
- package/src/i18n/notificationsTranslations.ts +0 -32
- package/src/i18n/onboardingTranslations.ts +0 -29
- package/src/i18n/sectionNavTranslations.ts +0 -20
- package/src/i18n/uiCoreTranslations.js +0 -29
- package/src/i18n/userMenuTranslations.ts +0 -5
- package/src/index.js +0 -158
- package/src/layout/PageLayout.jsx +0 -36
- package/src/layout/SectionNav.jsx +0 -320
- package/src/messaging/AttachmentList.jsx +0 -145
- package/src/messaging/Composer.jsx +0 -197
- package/src/messaging/ConversationLaunchers.jsx +0 -51
- package/src/messaging/ConversationList.jsx +0 -109
- package/src/messaging/DirectMessageLauncher.jsx +0 -112
- package/src/messaging/MessageBubble.jsx +0 -183
- package/src/messaging/MessagingProvider.jsx +0 -0
- package/src/messaging/MessagingScopeConfig.jsx +0 -24
- package/src/messaging/PollCard.jsx +0 -163
- package/src/messaging/ReactionBar.jsx +0 -43
- package/src/messaging/ReadTicks.jsx +0 -82
- package/src/messaging/Thread.jsx +0 -195
- package/src/messaging/api.js +0 -48
- package/src/messaging/composerImageCompression.js +0 -35
- package/src/messaging/linkifyText.jsx +0 -37
- package/src/notifications/NotificationBell.jsx +0 -84
- package/src/notifications/NotificationSettings.jsx +0 -330
- package/src/notifications/NotificationsProvider.jsx +0 -224
- package/src/notifications/PopupSurface.jsx +0 -105
- package/src/notifications/api.js +0 -55
- package/src/notifications/feedApi.js +0 -18
- package/src/notifications/realtime.jsx +0 -138
- package/src/notifications/serviceWorker/sw.js +0 -50
- package/src/onboarding/OnboardingProvider.jsx +0 -326
- package/src/onboarding/OnboardingWizard.jsx +0 -61
- package/src/onboarding/WizardDialogShell.jsx +0 -51
- package/src/onboarding/api.js +0 -13
- package/src/onboarding/stepSelection.js +0 -16
- package/src/onboarding/steps/BrowserPushStep.jsx +0 -121
- package/src/onboarding/steps/CompleteNameStep.jsx +0 -46
- package/src/onboarding/steps/CookieConsentStep.jsx +0 -133
- package/src/onboarding/steps/PwaInstallStep.jsx +0 -47
- package/src/pages/AccountPage.jsx +0 -394
- package/src/pages/LoginPage.jsx +0 -343
- package/src/pages/PasswordChangePage.jsx +0 -62
- package/src/pages/PasswordInvitePage.jsx +0 -177
- package/src/pages/PasswordResetRequestPage.jsx +0 -68
- package/src/pages/SignUpPage.jsx +0 -268
- package/src/pages/SignupConfirmPage.jsx +0 -155
- package/src/testing/checkKitIntegration.js +0 -233
- package/src/theme/createAppTheme.js +0 -423
- package/src/theme/fonts.js +0 -3
- package/src/theme/index.js +0 -21
- package/src/theme/kitSxRegistry.js +0 -285
- package/src/theme/themeCompleteness.js +0 -1045
- package/src/theme/tokens.js +0 -478
- package/src/utils/auth-errors.js +0 -76
- package/src/utils/authService.js +0 -223
- package/src/utils/resolveErrorText.js +0 -28
- package/src/utils/webauthn.js +0 -51
- package/tests/AccountPage.profileStatements.test.jsx +0 -162
- package/tests/AccountPage.test.jsx +0 -200
- package/tests/AuthContext.test.jsx +0 -129
- package/tests/BarChart.test.jsx +0 -112
- package/tests/BrowserPushStep.test.jsx +0 -110
- package/tests/ChartFrame.test.jsx +0 -336
- package/tests/ConversationList.test.jsx +0 -144
- package/tests/CookieConsentStep.test.jsx +0 -69
- package/tests/EmptyState.test.jsx +0 -62
- package/tests/LineChart.test.jsx +0 -80
- package/tests/MessagingProvider.test.jsx +0 -50
- package/tests/MobileBottomNav.test.jsx +0 -319
- package/tests/NotificationBell.test.jsx +0 -60
- package/tests/NotificationSettings.test.jsx +0 -276
- package/tests/NotificationsProvider.test.jsx +0 -240
- package/tests/OnboardingProvider.test.jsx +0 -294
- package/tests/OnboardingWizard.test.jsx +0 -143
- package/tests/PasswordChangeForm.test.jsx +0 -70
- package/tests/PasswordInvitePage.test.jsx +0 -142
- package/tests/PasswordSetForm.test.jsx +0 -70
- package/tests/PopupSurface.test.jsx +0 -142
- package/tests/ProfileComponent.refresh.test.jsx +0 -194
- package/tests/QrSignupManager.registrationContext.test.jsx +0 -106
- package/tests/QrSignupManagerInfoBox.test.jsx +0 -49
- package/tests/ScatterChart.test.jsx +0 -270
- package/tests/SectionNav.test.jsx +0 -363
- package/tests/SignUpPage.test.jsx +0 -208
- package/tests/SoftChip.test.jsx +0 -105
- package/tests/StatTile.test.jsx +0 -57
- package/tests/Thread.test.jsx +0 -151
- package/tests/TimeSeriesChart.test.jsx +0 -367
- package/tests/UserListComponent.test.jsx +0 -192
- package/tests/UserMenu.test.jsx +0 -171
- package/tests/apiClientLanguage.test.js +0 -33
- package/tests/apiClientPublicPaths.test.js +0 -178
- package/tests/authApiLogin.test.js +0 -42
- package/tests/authApiRegistration.test.js +0 -31
- package/tests/authErrorCodeRendering.test.jsx +0 -77
- package/tests/authErrors.test.js +0 -74
- package/tests/authTranslations.test.js +0 -74
- package/tests/chart-api-census.test.js +0 -366
- package/tests/chartAxisTickBand.test.jsx +0 -95
- package/tests/chartDefaults.test.js +0 -902
- package/tests/chartLabels.test.js +0 -50
- package/tests/chartsPalette.test.js +0 -99
- package/tests/chatApi.test.js +0 -48
- package/tests/chatDriverNames.test.jsx +0 -71
- package/tests/chatExports.test.js +0 -3
- package/tests/chatList.test.jsx +0 -84
- package/tests/chatPicker.test.jsx +0 -82
- package/tests/chatSocket.test.jsx +0 -26
- package/tests/chatThread.test.jsx +0 -116
- package/tests/chatTranslations.test.js +0 -47
- package/tests/chatWorkspace.test.jsx +0 -234
- package/tests/checkKitIntegration.test.jsx +0 -219
- package/tests/createAppTheme.test.js +0 -132
- package/tests/createAppThemeDark.test.js +0 -127
- package/tests/createAppThemeLightGuard.test.js +0 -40
- package/tests/createAppThemePageContrast.test.js +0 -108
- package/tests/createAppThemeTypography.test.js +0 -53
- package/tests/errorCallSiteGuard.test.js +0 -32
- package/tests/exportChart.test.js +0 -313
- package/tests/feedApi.test.js +0 -46
- package/tests/fixtures/light-theme-baseline.json +0 -2234
- package/tests/i18nAggregate.test.js +0 -120
- package/tests/i18nGermanVariant.test.js +0 -65
- package/tests/loginPageErrorResolution.test.jsx +0 -102
- package/tests/messagingApi.test.js +0 -32
- package/tests/messagingChunk6.test.jsx +0 -116
- package/tests/messagingCompactBubble.test.jsx +0 -64
- package/tests/messagingComposer.test.jsx +0 -136
- package/tests/messagingComposerChunk5.test.jsx +0 -113
- package/tests/messagingContractConformance.test.js +0 -69
- package/tests/messagingDirectMessageLauncher.test.jsx +0 -151
- package/tests/messagingExports.test.js +0 -21
- package/tests/messagingInteractions.test.jsx +0 -136
- package/tests/messagingLastMessagePreview.test.jsx +0 -93
- package/tests/messagingLinkify.test.jsx +0 -51
- package/tests/messagingMessageActions.test.jsx +0 -163
- package/tests/messagingMsg6.test.jsx +0 -105
- package/tests/messagingMsg6f.test.jsx +0 -155
- package/tests/messagingMsg6g.test.jsx +0 -166
- package/tests/messagingMsg6h.test.jsx +0 -136
- package/tests/messagingMsg6i.test.jsx +0 -129
- package/tests/messagingPollRendering.test.jsx +0 -142
- package/tests/messagingReactionMerge.test.jsx +0 -77
- package/tests/messagingReadTicksTooltip.test.jsx +0 -36
- package/tests/messagingThreadReceipts.test.jsx +0 -178
- package/tests/messagingThreadingQuoting.test.jsx +0 -87
- package/tests/messagingUnreadLifecycle.test.jsx +0 -78
- package/tests/notificationsApi.test.js +0 -41
- package/tests/notificationsExports.test.js +0 -34
- package/tests/onboardingDescriptors.test.js +0 -132
- package/tests/packageTreeShaking.test.js +0 -126
- package/tests/realtime.test.jsx +0 -219
- package/tests/resolveErrorText.test.js +0 -72
- package/tests/stepSelection.test.js +0 -53
- package/tests/themeCompleteness.test.js +0 -626
- package/tests/timeSeriesLegend.test.js +0 -99
- package/tests/userScreenTranslations.test.jsx +0 -218
- package/tsconfig.build.json +0 -18
- package/vite.config.mjs +0 -6
- package/work-orders/AUTH-1.md +0 -74
- package/work-orders/AUTH-2.md +0 -74
- package/work-orders/AUTH-4.md +0 -183
- package/work-orders/AUTH-5.md +0 -154
- package/work-orders/AUTH-7.md +0 -181
- package/work-orders/AUTH-8.md +0 -184
- package/work-orders/CHART-1.md +0 -285
- package/work-orders/CHART-2.md +0 -222
- package/work-orders/CHART-3.md +0 -163
- package/work-orders/CHART-4.md +0 -203
- package/work-orders/CHART-5.md +0 -226
- package/work-orders/CHART-6.md +0 -428
- package/work-orders/CHART-7.md +0 -175
- package/work-orders/DX-1.md +0 -169
- package/work-orders/DX-2.md +0 -188
- package/work-orders/DX-3.md +0 -195
- package/work-orders/I18N-1.md +0 -254
- package/work-orders/I18N-3.md +0 -207
- package/work-orders/MSG-3.md +0 -375
- package/work-orders/MSG-3b.md +0 -1002
- package/work-orders/MSG-3c.md +0 -378
- package/work-orders/MSG-3d.md +0 -268
- package/work-orders/MSG-3e.md +0 -164
- package/work-orders/MSG-3f.md +0 -391
- package/work-orders/MSG-6e.md +0 -308
- package/work-orders/MSG-6f.md +0 -214
- package/work-orders/MSG-6g.md +0 -244
- package/work-orders/MSG-6h.md +0 -238
- package/work-orders/MSG-6i.md +0 -271
- package/work-orders/MSG-6j.md +0 -117
- package/work-orders/NOTIF-12.md +0 -214
- package/work-orders/NOTIF-13.md +0 -142
- package/work-orders/NOTIF-14.md +0 -151
- package/work-orders/PRIM-1.md +0 -177
- package/work-orders/SHELL-1.md +0 -255
- package/work-orders/SHELL-2.md +0 -197
- package/work-orders/SHELL-3.md +0 -359
- package/work-orders/SHELL-4.md +0 -351
- package/work-orders/SHELL-5.md +0 -345
- package/work-orders/SHELL-6.md +0 -210
- package/work-orders/THEME-1.md +0 -587
- package/work-orders/THEME-10.md +0 -303
- package/work-orders/THEME-11.md +0 -208
- package/work-orders/THEME-2.md +0 -339
- package/work-orders/THEME-3.md +0 -210
- package/work-orders/THEME-4.md +0 -271
- package/work-orders/THEME-5.md +0 -341
- package/work-orders/THEME-6.md +0 -247
- package/work-orders/THEME-7.md +0 -82
- package/work-orders/THEME-8.md +0 -211
- package/work-orders/THEME-9.md +0 -194
- package/work-orders/UCM-AUTH-10.md +0 -490
- package/work-orders/UCM-AUTH-11.md +0 -185
- package/work-orders/UCM-AUTH-12.md +0 -98
- package/work-orders/UCM-AUTH-13.md +0 -47
- package/work-orders/UCM-AUTH-9.md +0 -207
- package/work-orders/UCM-CHART-10.md +0 -150
- package/work-orders/UCM-CHART-11.md +0 -132
- package/work-orders/UCM-CHART-12.md +0 -348
- package/work-orders/UCM-CHART-13.md +0 -122
- package/work-orders/UCM-CHART-14.md +0 -198
- package/work-orders/UCM-CHART-15.md +0 -187
- package/work-orders/UCM-CHART-16.md +0 -169
- package/work-orders/UCM-CHART-17.md +0 -195
- package/work-orders/UCM-CHART-18.md +0 -200
- package/work-orders/UCM-CHART-8.md +0 -147
- package/work-orders/UCM-CHART-9.md +0 -265
- package/work-orders/UCM-CHAT-1.md +0 -190
- package/work-orders/UCM-CHAT-2.md +0 -75
- package/work-orders/UCM-CI-1.md +0 -65
- package/work-orders/UCM-DEP-1.md +0 -63
- package/work-orders/UCM-I18N-4.md +0 -226
- package/work-orders/UCM-I18N-5.md +0 -211
- package/work-orders/UCM-MSG-20.md +0 -192
- package/work-orders/UCM-PRIM-2.md +0 -114
- package/work-orders/UCM-SHELL-7.md +0 -134
- package/work-orders/UCM-SHELL-8.md +0 -125
- package/work-orders/UCM-TEST-1.md +0 -304
- package/work-orders/UCM-THEME-13.md +0 -69
- package/work-orders/UCM-THEME-14.md +0 -312
- package/work-orders/UCM-THEME-15.md +0 -356
- package/work-orders/UCM-THEME-16.md +0 -80
- package/work-orders/UCM-THEME-17.md +0 -111
- package/work-orders/UCM-THEME-18.md +0 -184
- package/work-orders/assets/AUTH-4-user-list-card.html +0 -407
- package/work-orders/assets/PRIM-1-dashboard-primitives.html +0 -624
- package/work-orders/assets/SHELL-2-account-navigation.html +0 -1159
- package/work-orders/assets/THEME-1-baseline-reference-sheet.html +0 -1352
- package/work-orders/assets/UCM-CHAT-1-ai-chat.html +0 -447
- package/work-orders/assets/UCM-THEME-13-dark-prototype.html +0 -390
- package/work-orders/assets/UCM-THEME-17-type-scale.html +0 -391
package/work-orders/NOTIF-13.md
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
# WORK ORDER NOTIF-13 (ui-core-micha + django-core-micha) — Layer-1 transport extraction
|
|
2
|
-
|
|
3
|
-
**EXECUTION DIRECTIVE.** Implement through `codex exec` in the background — invoked **directly via
|
|
4
|
-
Bash** (never the `debugger`/`*_coder` Agent wrappers) with **both** flags `--skip-git-repo-check`
|
|
5
|
-
and `--dangerously-bypass-approvals-and-sandbox`, prompt passed as a positional argument from this
|
|
6
|
-
file. Fall back to direct Claude implementation only on Codex quota / rate-limit / non-zero exit.
|
|
7
|
-
|
|
8
|
-
Feature-level WO (Expertenchat envelope). The seams below are **verified against the landed code**
|
|
9
|
-
(ucm 2.12.0, dcm 2.32.0) and are the required scope. The Orchestrator fills the exact line map.
|
|
10
|
-
|
|
11
|
-
## TIER
|
|
12
|
-
Tier 2 — restructures a provider that **cockpit consumes in production**, and changes a WebSocket
|
|
13
|
-
message contract shared across apps. Independent `reviewer` mandatory. Per AGENTS.md Tier 2,
|
|
14
|
-
`sec_reviewer` is NOT auto-included; request it only if the operator asks.
|
|
15
|
-
|
|
16
|
-
## GOAL
|
|
17
|
-
Turn ucm's notification-only socket into a generic **Layer-1 realtime primitive**: one socket,
|
|
18
|
-
subscribe-by-message-type, so a second domain (messaging) can ride it without being misread as a
|
|
19
|
-
notification. **Additive**: `NotificationsProvider` remains the sole mount point and its public
|
|
20
|
-
context value is unchanged (operator decision 2026-07-29).
|
|
21
|
-
|
|
22
|
-
## EXPECTED OUTCOME
|
|
23
|
-
- ucm exports a `useRealtime()`-style subscribe primitive alongside the unchanged `useNotifications()`.
|
|
24
|
-
- An unknown/unsubscribed message type is **ignored** instead of being appended to the notification feed.
|
|
25
|
-
- No app has to change its provider tree. cockpit and jg keep working untouched.
|
|
26
|
-
- NOTIF-15 (jg bell adoption, retiring jg's local context) becomes implementable; Phase B messaging
|
|
27
|
-
gets its substrate.
|
|
28
|
-
|
|
29
|
-
## CONTEXT PACKAGE — verified current state
|
|
30
|
-
|
|
31
|
-
**ucm `src/notifications/NotificationsProvider.jsx` (232 LOC)** owns *both* the socket and the
|
|
32
|
-
notifications domain state:
|
|
33
|
-
- socket ownership: `socketRef`, `connect()`, exponential backoff `INITIAL_BACKOFF_MS` 1s →
|
|
34
|
-
`MAX_BACKOFF_MS` 30s, auth-gated effect keyed on `[authenticated, refresh, replaceNotifications, wsUrlBase]` (`:87-179`).
|
|
35
|
-
- `handleMessage` (`:103-133`) dispatches on exactly **one** envelope type, `notification.status` (`:111`).
|
|
36
|
-
- **The blocker:** everything else falls through to `normalizeNotificationPush(data)` and is appended
|
|
37
|
-
to the feed with `unreadCount + 1` (`:130-132`). A messaging payload on this socket becomes a fake
|
|
38
|
-
notification today.
|
|
39
|
-
- **Field overload — the reason a third stream cannot simply be added:** in the status branch
|
|
40
|
-
`data.type` is the *envelope* type; in the new-notification branch `normalizeNotificationPush`
|
|
41
|
-
reads `data.type` as the *domain* notification-type key (`:25`). One field, two meanings. The
|
|
42
|
-
envelope contract must resolve this without breaking either reading.
|
|
43
|
-
- WS URL hardcoded to `/ws/notifications/` (`:36`, `:40`).
|
|
44
|
-
- exports in `src/index.js:48` (`NotificationsProvider`, `useNotifications`).
|
|
45
|
-
|
|
46
|
-
**dcm transport is ALREADY generic — correct the roadmap wording here.** The roadmap §4 calls for a
|
|
47
|
-
"dcm multi-stream consumer"; that overstates the dcm work:
|
|
48
|
-
- `notifications/delivery.py::push_to_users(users, payload)` (`:20-43`) fans an **arbitrary** payload
|
|
49
|
-
into the per-user group `notifications_user_{id}`.
|
|
50
|
-
- `notifications/consumers.py::NotificationConsumer.message()` (`:21-22`) is `send_json(event["payload"])` —
|
|
51
|
-
fully stream-agnostic. jg's messaging **already** rides this same group via `push_to_users`.
|
|
52
|
-
- The only dcm-side envelope producer is `notifications/views.py:235` (`"type": "notification.status"`);
|
|
53
|
-
the contract is documented at `views.py:3`.
|
|
54
|
-
- **So the dcm work is the envelope contract + a thin authoring helper + doc — NOT a consumer rewrite.**
|
|
55
|
-
|
|
56
|
-
**Reference implementation to promote:** jg's `frontend/src/context/NotificationsContext.jsx` (91 LOC,
|
|
57
|
-
docstring "WebSocket owner only") already does exactly this multiplexing with
|
|
58
|
-
`registerMessageCallback` / `registerChatCallback`. Read it for the shape. **Do not edit jg here.**
|
|
59
|
-
|
|
60
|
-
## SCOPE
|
|
61
|
-
|
|
62
|
-
**A. ucm — extract the socket into an internal realtime core.** Single owner; preserve the existing
|
|
63
|
-
backoff, reconnect, auth-gating and cleanup semantics **verbatim**. Expose
|
|
64
|
-
`subscribe(type, handler) -> unsubscribe`.
|
|
65
|
-
|
|
66
|
-
**B. ucm — `NotificationsProvider` becomes the first subscriber.** It registers handlers for the
|
|
67
|
-
notification envelope types. Its returned context value stays **exactly**
|
|
68
|
-
`{ notifications, unreadCount, markSeen, markDismissed, markDone, refresh }`.
|
|
69
|
-
|
|
70
|
-
**C. Envelope contract.** Introduce an explicit envelope discriminator that does not collide with the
|
|
71
|
-
domain `type` field (see the field-overload note above). Unknown / unsubscribed envelope types are
|
|
72
|
-
**ignored** — the catch-all at `:130-132` dies. This is a deliberate behaviour change and must be
|
|
73
|
-
called out in the CHANGELOG.
|
|
74
|
-
|
|
75
|
-
**D. Backward compatibility in BOTH directions** — apps pin dcm and ucm independently, so mismatched
|
|
76
|
-
versions are normal:
|
|
77
|
-
- *new ucm + old dcm* (payload has no envelope field): must still be recognised as a new notification,
|
|
78
|
-
exactly as today. Without this the feed silently goes dead against an un-bumped backend.
|
|
79
|
-
- *old ucm + new dcm*: dcm must keep emitting the legacy fields so a ucm 2.12.0 client is unaffected
|
|
80
|
-
(**cockpit runs 2.12.0 in staging/prod**).
|
|
81
|
-
|
|
82
|
-
**E. dcm — formalize the contract.** Document the envelope next to `views.py:3` and add a thin
|
|
83
|
-
authoring helper so producers stop hand-rolling the dict. `push_to_users`'s signature stays unchanged.
|
|
84
|
-
|
|
85
|
-
## DO NOT TOUCH
|
|
86
|
-
- **jg-ferien** (`NotificationsContext.jsx`, `MessagingContext.jsx`) — that is NOTIF-15.
|
|
87
|
-
- The messaging domain itself — Phase B (`MSG-*`).
|
|
88
|
-
- `NotificationBell.jsx`, `NotificationSettings.jsx`, `feedApi.js`, the `feed/*` REST contract.
|
|
89
|
-
- `BaseSecureConsumer` / S112 permission model, `IsAuthenticatedWs`, the group name `notifications_user_{id}`.
|
|
90
|
-
- **No standalone `RealtimeProvider` export that apps must mount.** Operator decision 2026-07-29:
|
|
91
|
-
additive only. A standalone provider is a later, separate WO if a second consumer ever needs it.
|
|
92
|
-
- Reconnect/backoff semantics and the single-socket-owner invariant — preserve, do not "improve".
|
|
93
|
-
- Schema, migrations, dependencies, CI.
|
|
94
|
-
- **No app pin bumps** in this WO (cockpit/jg stay where they are).
|
|
95
|
-
|
|
96
|
-
## RISKS
|
|
97
|
-
- **Silent feed death** if C lands without D: an envelope-less payload from an older dcm would be
|
|
98
|
-
ignored instead of shown. D is not optional.
|
|
99
|
-
- **cockpit is a live consumer** at ucm 2.12.0 — any change to the returned context shape breaks it.
|
|
100
|
-
- **Subscription leaks / double delivery:** `subscribe` must return a stable unsubscribe, and handlers
|
|
101
|
-
must **survive reconnects** without the caller re-registering them.
|
|
102
|
-
- **Reconnect loop:** the socket effect currently depends on `refresh` / `replaceNotifications`;
|
|
103
|
-
moving the socket out must not make the socket re-create on every feed update.
|
|
104
|
-
|
|
105
|
-
## REQUIRED TESTS / ACCEPTANCE
|
|
106
|
-
ucm runs `vitest` (`pnpm test`) and has **no tests under `src/notifications/` today** — this WO adds
|
|
107
|
-
the first ones:
|
|
108
|
-
- an unknown envelope type is ignored: feed and `unreadCount` both untouched (the regression that
|
|
109
|
-
protects messaging from being misread as a notification);
|
|
110
|
-
- two subscribers on distinct types each receive only their own messages, from **one** socket —
|
|
111
|
-
assert exactly one `WebSocket` was constructed;
|
|
112
|
-
- `unsubscribe` stops delivery for that subscriber and does not affect the others;
|
|
113
|
-
- handlers keep receiving after a close → backoff → reopen cycle, with no re-registration by the caller;
|
|
114
|
-
- a legacy envelope-less payload still lands in the feed (backward-compat D);
|
|
115
|
-
- `notification.status` handling and the `unreadCount` delta are unchanged — this guards the NOTIF-6
|
|
116
|
-
R1 fix (badge went stale after a cross-device mark);
|
|
117
|
-
- `NotificationsProvider`'s public context value is unchanged.
|
|
118
|
-
|
|
119
|
-
Plus `pnpm build` (tsc) green. If E lands in dcm, run the dcm notifications `pytest` package.
|
|
120
|
-
|
|
121
|
-
## RELEASE
|
|
122
|
-
ucm minor bump + publish (publish-from-main). dcm only if E lands there — then publish-from-main with
|
|
123
|
-
a **registry live-check before any app pin bump**. No app pin bumps in this WO.
|
|
124
|
-
|
|
125
|
-
## TARGET REPO / WORKING DIRECTORY
|
|
126
|
-
- Primary: `C:\Users\biglmi\Documents\webapps\ui-core-micha`
|
|
127
|
-
- Secondary (scope E only): `C:\Users\biglmi\Documents\webapps\django-core-micha`
|
|
128
|
-
- Never the workspace root `…\webapps`.
|
|
129
|
-
Both are platform repos: commit directly to the trunk, no feature branches.
|
|
130
|
-
|
|
131
|
-
## PROGRESS CONTRACT
|
|
132
|
-
Emit a `PLAN: <step1> | <step2> | …` line up front, then a single-line
|
|
133
|
-
`PROGRESS: [<n>/<total>] <present-tense action>` **before every relevant action** (file opened, file
|
|
134
|
-
edited, command/test run) and `PROGRESS: [<n>/<total>] done` on step completion, spaced so no gap
|
|
135
|
-
exceeds ~2 min. stdout unbuffered. Exactly one final `RESULT: DONE|BLOCKED <reason>`.
|
|
136
|
-
|
|
137
|
-
## MINI-HANDOVER (paste into a fresh Orchestrator session)
|
|
138
|
-
```
|
|
139
|
-
Orchestrator: implement work-orders/NOTIF-13.md in ui-core-micha (main; scope E also touches
|
|
140
|
-
django-core-micha). git pull first, read the WO, then follow orchestrate-codex (Codex-first, own
|
|
141
|
-
independent review, commit on green).
|
|
142
|
-
```
|
package/work-orders/NOTIF-14.md
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
# WORK ORDER NOTIF-14 (ui-core-micha) — push-preview toggle in NotificationSettings
|
|
2
|
-
|
|
3
|
-
**EXECUTION DIRECTIVE.** If you are the implementer reading this as your own spec, this section is
|
|
4
|
-
not addressed to you — it tells the Orchestrator how to invoke you; you ARE that invocation, do not
|
|
5
|
-
shell out to `codex exec`. Orchestrator: implement through `codex exec` in the background, invoked
|
|
6
|
-
**directly via Bash** (never the `debugger`/`*_coder` Agent wrappers) with **both** flags
|
|
7
|
-
`--skip-git-repo-check` and `--dangerously-bypass-approvals-and-sandbox`, prompt passed as a
|
|
8
|
-
positional argument from this file. Fall back to direct Claude implementation only on Codex quota /
|
|
9
|
-
rate-limit / non-zero exit.
|
|
10
|
-
|
|
11
|
-
Feature-level WO (Expertenchat envelope). Companion to `django-core-micha`'s `work-orders/MSG-13.md`
|
|
12
|
-
(landed `39135e9` on dcm `main`), whose Scope E explicitly said: "The `Switch` in `ui-core-micha`'s
|
|
13
|
-
`NotificationSettings.jsx` is a companion change and is NOT deliverable from this repo — flag it in
|
|
14
|
-
the completion note so a `ui-core-micha` WO gets written." This is that WO.
|
|
15
|
-
|
|
16
|
-
## TIER
|
|
17
|
-
Tier 2 — this repo IS a shared-core surface (`ui-core-micha`), which per AGENTS.md forces Tier 2
|
|
18
|
-
regardless of change size. Independent `reviewer` mandatory. No `sec_reviewer` needed: the security
|
|
19
|
-
question (should message content go in a push at all) was already ruled by the operator in dcm's
|
|
20
|
-
MSG-13; this WO only wires an existing, already-shipped backend preference into the existing
|
|
21
|
-
settings UI — no new exposure, no new endpoint, no new data crossing a trust boundary.
|
|
22
|
-
|
|
23
|
-
## GOAL
|
|
24
|
-
dcm's MSG-13 added a per-user account-level preference, `NotificationPreference.push_preview_opt_in`
|
|
25
|
-
(default `True`), already exposed read/write on the existing `notifications/preferences/` endpoint
|
|
26
|
-
(`NotificationPreferenceSerializer` fields: `email_opt_in`, `push_opt_in`, `push_preview_opt_in`).
|
|
27
|
-
When off, dcm's `PushDispatcher` sends a content-free push body (no sender name, no message text) —
|
|
28
|
-
the notification still arrives, but its preview is hidden. Nothing in ucm reads or writes this field
|
|
29
|
-
yet, so a user has no way to turn it off (or back on) — a per-user preference sitting dead on the
|
|
30
|
-
backend with no UI path to reach it.
|
|
31
|
-
|
|
32
|
-
## EXPECTED OUTCOME
|
|
33
|
-
- `NotificationSettings.jsx` gains a third toggle, alongside the existing email/push `Switch`es,
|
|
34
|
-
bound to `push_preview_opt_in` via the same generic `patchNotificationPreferences()` call already
|
|
35
|
-
used for the other two (no new API function needed — see Context Package).
|
|
36
|
-
- The toggle reflects the loaded preference correctly, **including the default-on case**: a fresh
|
|
37
|
-
account (or a preferences payload predating this field) has never set `push_preview_opt_in`
|
|
38
|
-
explicitly, and this MUST still render as checked/on. `Boolean(preferences?.push_preview_opt_in)`
|
|
39
|
-
— the pattern the existing two switches use — is **wrong** here, because `Boolean(undefined) ===
|
|
40
|
-
false`; that would silently show "preview off" for a user whose real, backend-defaulted state is
|
|
41
|
-
"preview on" (the exact class of default-mismatch bug MSG-13 itself was about, one level up).
|
|
42
|
-
The correct check is `preferences?.push_preview_opt_in !== false`.
|
|
43
|
-
- Toggling calls `patchNotificationPreferences({ push_preview_opt_in: <bool> })` and updates local
|
|
44
|
-
state from the response, mirroring `handleEmailToggle`'s existing shape exactly (loading flag,
|
|
45
|
-
error handling, no confirmation dialog).
|
|
46
|
-
- **UX placement decision (Expertenchat call, confirm or override at Approval Gate #1):** render the
|
|
47
|
-
preview toggle directly under the push toggle (same `Box`/`Divider` block, not a new section), and
|
|
48
|
-
**disable it when push notifications are not actually enabled for this account**
|
|
49
|
-
(`!preferences?.push_opt_in`) — a preview setting is meaningless when no push is being sent at all,
|
|
50
|
-
and leaving it enabled-but-inert invites a user to "fix" a push they never receive by toggling a
|
|
51
|
-
setting that does nothing. Gate on the account-level `push_opt_in` (matches dcm's own
|
|
52
|
-
`is_channel_enabled` gate for the `push` channel), not on this device's `pushSubscribed` — a user
|
|
53
|
-
with push enabled on another device but not this one should still be able to manage their preview
|
|
54
|
-
preference here.
|
|
55
|
-
|
|
56
|
-
## CONTEXT PACKAGE — verified current state
|
|
57
|
-
|
|
58
|
-
**`src/notifications/NotificationSettings.jsx`** (194 lines) already renders two independent
|
|
59
|
-
preference switches against the same preferences object:
|
|
60
|
-
- `handleEmailToggle` (`:81-93`) — the exact shape to mirror: `setSavingEmail(true)` →
|
|
61
|
-
`patchNotificationPreferences({ email_opt_in })` → `setPreferences(updated)` → catch sets
|
|
62
|
-
`error` → `finally` resets the saving flag.
|
|
63
|
-
- The push switch (`:176-187`) is more involved (subscribe/unsubscribe device flow) — **do not
|
|
64
|
-
mirror that one**, the preview toggle is a plain preference flip like email, not a device
|
|
65
|
-
subscription.
|
|
66
|
-
- `preferences` state (`:37`) is the raw object returned by `getNotificationPreferences()`
|
|
67
|
-
(`GET /api/notifications/preferences/`) and updated in place by
|
|
68
|
-
`patchNotificationPreferences()` (`PATCH`, same URL, generic passthrough — accepts any subset of
|
|
69
|
-
serializer fields, **no new API function needed**, see `src/notifications/api.js:6-13`).
|
|
70
|
-
- Render block to extend is `:162-188` (the email `Box`, a `Divider`, the push `Box`). Add a third
|
|
71
|
-
`Box` with a `FormControlLabel`+`Switch`, same structural pattern as the email one (`:163-168`) —
|
|
72
|
-
`checked`, `onChange`, `disabled`, then a label with a primary `Typography` + a `caption`
|
|
73
|
-
`Typography` hint underneath.
|
|
74
|
-
|
|
75
|
-
**`src/notifications/api.js`** — `patchNotificationPreferences(patch)` (`:11-14`) is a bare
|
|
76
|
-
passthrough `PATCH`; no change needed here.
|
|
77
|
-
|
|
78
|
-
**i18n**: `src/i18n/notificationsTranslations.ts` — existing `NotificationSettings.*` keys (`:5-19`)
|
|
79
|
-
cover `EMAIL_LABEL`/`EMAIL_HINT`/`PUSH_LABEL`/`PUSH_HINT` etc. across four languages (`de`, `fr`,
|
|
80
|
-
`en`, `sw`). Add two new keys, e.g. `NotificationSettings.PUSH_PREVIEW_LABEL` and
|
|
81
|
-
`NotificationSettings.PUSH_PREVIEW_HINT`, in the same four-language shape. The hint text should say
|
|
82
|
-
what changes when off (no sender name or message text in the push body — the message itself still
|
|
83
|
-
arrives) without needing to explain the underlying push-encryption reasoning from dcm's MSG-13 (that
|
|
84
|
-
belongs to dcm, not this UI copy).
|
|
85
|
-
|
|
86
|
-
**Tests**: `tests/NotificationSettings.test.jsx` already mocks `../src/notifications/api` in full
|
|
87
|
-
(`:6-13`) and stubs `react-i18next` to return raw keys (`:16`) — extend the existing
|
|
88
|
-
`getNotificationPreferences`/`patchNotificationPreferences` mocks, do not add a new mock module.
|
|
89
|
-
|
|
90
|
-
## NON-GOALS / DO NOT TOUCH
|
|
91
|
-
- Do not touch the push subscribe/unsubscribe device flow (`handleEnablePush`/`handleDisablePush`,
|
|
92
|
-
`:95-144`) or anything in `getPushSupport`/`getIosInstallState`.
|
|
93
|
-
- Do not touch `django-core-micha` — the backend half (field, migration, serializer, dispatch gating)
|
|
94
|
-
already shipped in dcm MSG-13 (`39135e9` on `main`). This WO is UI-only.
|
|
95
|
-
- Do not add a confirmation dialog or any new component beyond the one additional `Switch` block —
|
|
96
|
-
match the existing two switches' bare, no-confirmation UX exactly.
|
|
97
|
-
- Do not change what dcm's push body renders when the preference is off (already correct and
|
|
98
|
-
reviewed in dcm MSG-13) — this WO is only "can the user reach the setting", not the setting's effect.
|
|
99
|
-
|
|
100
|
-
## RISKS
|
|
101
|
-
- The `Boolean(...)` vs `!== false` default-mismatch trap above is the main risk in this WO — call it
|
|
102
|
-
out explicitly in the PR/commit and in the required test, since it is easy to copy-paste the
|
|
103
|
-
existing `Boolean(preferences?.push_opt_in)` pattern without noticing the two fields have opposite
|
|
104
|
-
defaults (`push_opt_in` defaults `False` on the backend, `push_preview_opt_in` defaults `True`).
|
|
105
|
-
- Low blast radius otherwise: one existing, already-authenticated, already-scoped-to-`request.user`
|
|
106
|
-
endpoint; no new permission surface; additive UI only.
|
|
107
|
-
|
|
108
|
-
## REQUIRED TESTS TO WRITE
|
|
109
|
-
Narrow, mirroring `tests/NotificationSettings.test.jsx`'s existing style and mocks.
|
|
110
|
-
|
|
111
|
-
1. The preview switch renders **checked** when `getNotificationPreferences()` resolves with
|
|
112
|
-
`push_preview_opt_in: true`, **and also** when it resolves with the field **absent entirely**
|
|
113
|
-
(the default-on, non-vacuity case — must fail if the implementation uses `Boolean(...)` instead
|
|
114
|
-
of the `!== false` check).
|
|
115
|
-
2. The preview switch renders **unchecked** when `push_preview_opt_in: false`.
|
|
116
|
-
3. Toggling the switch calls `patchNotificationPreferences({ push_preview_opt_in: <new value> })`
|
|
117
|
-
and re-renders from the mocked response.
|
|
118
|
-
4. The preview switch is **disabled** when `preferences.push_opt_in` is `false`, and **enabled**
|
|
119
|
-
when it is `true` — regardless of this device's own `pushSubscribed` state (per the UX placement
|
|
120
|
-
decision above).
|
|
121
|
-
|
|
122
|
-
**Non-vacuity:** test 1's absent-field case must fail against a naive `Boolean(preferences?.…)`
|
|
123
|
-
implementation — prove it before considering this WO done.
|
|
124
|
-
|
|
125
|
-
## TEST SCOPE FOR THE GATE (orchestrator)
|
|
126
|
-
`tests/NotificationSettings.test.jsx` only — no other suite touches this component.
|
|
127
|
-
|
|
128
|
-
## TARGET REPO
|
|
129
|
-
`C:\Users\biglmi\Documents\webapps\ui-core-micha`. Branch `main` (no `develop` in this repo — verify
|
|
130
|
-
with `git branch -a` before starting in case that has changed). Publish + version bump per the
|
|
131
|
-
repo's release flow if this ships as its own npm release, or bundle with the next ucm release per
|
|
132
|
-
operator direction.
|
|
133
|
-
|
|
134
|
-
## MINI-HANDOVER (pastable)
|
|
135
|
-
|
|
136
|
-
> Repo: `C:\Users\biglmi\Documents\webapps\ui-core-micha` (branch `main`). Work order:
|
|
137
|
-
> `work-orders/NOTIF-14.md` — read it fully, then follow the `orchestrate-codex` skill.
|
|
138
|
-
>
|
|
139
|
-
> Adds the third `Switch` to `NotificationSettings.jsx` for dcm MSG-13's `push_preview_opt_in`
|
|
140
|
-
> preference (already live on the backend, `django-core-micha` `39135e9`). The one thing that will
|
|
141
|
-
> silently ship wrong if rushed: this field defaults to `True`, opposite of `push_opt_in`'s `False`
|
|
142
|
-
> default — the existing `Boolean(preferences?.field)` pattern used for the other two switches
|
|
143
|
-
> reads a missing/undefined field as `false` and would show "preview off" for every account that
|
|
144
|
-
> never explicitly touched the setting. Use `preferences?.push_preview_opt_in !== false` instead,
|
|
145
|
-
> and make sure the required test proves it (assert checked=true when the field is absent from the
|
|
146
|
-
> mocked API response, not just when it's explicitly `true`).
|
|
147
|
-
|
|
148
|
-
## PROGRESS CONTRACT
|
|
149
|
-
Emit `PLAN: <steps>` up front, then a single-line `PROGRESS: [<n>/<total>] <action>` before every
|
|
150
|
-
relevant action and `PROGRESS: [<n>/<total>] done` on completion, spaced so no gap exceeds ~2 min,
|
|
151
|
-
stdout unbuffered, and exactly one final `RESULT: DONE|BLOCKED <reason>`.
|
package/work-orders/PRIM-1.md
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
# PRIM-1 — Promote `StatTile` and `SoftChip` into the shared kit
|
|
2
|
-
|
|
3
|
-
**Target repo:** `ui-core-micha` (branch `main`)
|
|
4
|
-
**Tier:** 3 — shared-core.
|
|
5
|
-
|
|
6
|
-
New register prefix `PRIM-*`: shared dashboard/data primitives — small presentational components that
|
|
7
|
-
are neither chart (`CHART-*`) nor app chrome (`SHELL-*`).
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## A. Envelope
|
|
12
|
-
|
|
13
|
-
### Goal
|
|
14
|
-
|
|
15
|
-
Both components already exist **twice**, built independently, and the second build in each case is an
|
|
16
|
-
**inline component inside a page** rather than a file — which is why a filename search misses it:
|
|
17
|
-
|
|
18
|
-
| Component | First build | Second build |
|
|
19
|
-
|---|---|---|
|
|
20
|
-
| `StatTile` | `hram/frontend/src/components/ResearchComponents/StatTile.jsx` (36 lines) | `fitness-monitor/frontend/src/pages/BodyHistoryPage.jsx:554-568` |
|
|
21
|
-
| `SoftChip` | `hram/frontend/src/components/ResearchComponents/SoftChip.jsx` (67 lines) | `cockpit/frontend/src/cockpit/BoardView.jsx:65-73` (the lane pill) |
|
|
22
|
-
|
|
23
|
-
Promote both into the kit as the single implementation. The promotion trigger is met on evidence — two
|
|
24
|
-
independent builds each, and a third app in view for each.
|
|
25
|
-
|
|
26
|
-
### The visual spec, and the seven decisions in it
|
|
27
|
-
|
|
28
|
-
**Prototype:** [`work-orders/assets/PRIM-1-dashboard-primitives.html`](work-orders/assets/PRIM-1-dashboard-primitives.html).
|
|
29
|
-
It renders the promoted spec at 375 px and 1280 px, keeps both original builds side by side as the
|
|
30
|
-
record of what changed, and computes its contrast ratios in-page against the *blended* fill.
|
|
31
|
-
|
|
32
|
-
The two builds diverged in seven places. All seven were decided by the operator on 2026-08-12, and the
|
|
33
|
-
prototype already reflects them:
|
|
34
|
-
|
|
35
|
-
| | Decision | Which app changes |
|
|
36
|
-
|---|---|---|
|
|
37
|
-
| StatTile value size | **`h5` — 18/600** | hram, 16 → 18 |
|
|
38
|
-
| StatTile digits | **`tabular-nums`, always** — it is what makes a KPI row read as a row | fitness-monitor |
|
|
39
|
-
| StatTile row behaviour | **`flex: 1` with a `minWidth` floor** — one rule covering both existing behaviours, no prop | hram, tiles now share the row |
|
|
40
|
-
| StatTile `accent` | **kept** — 2 px `primary.main`; without it fitness-monitor cannot adopt | neither |
|
|
41
|
-
| StatTile padding | **responsive `{xs: 1, sm: 1.5}`** | hram, was fixed |
|
|
42
|
-
| SoftChip `status` typography | **`overline` — 11/600, uppercase, .4 px tracking** | hram (`passed` → `PASSED`); cockpit drops 700 → 600 |
|
|
43
|
-
| SoftChip leading element + border | **dot by default, any icon via prop; border optional** | neither — both adopt unchanged |
|
|
44
|
-
|
|
45
|
-
**Why `overline` rather than a new variant:** cockpit's build independently arrived at 11-with-uppercase-
|
|
46
|
-
and-.4 px-tracking, which is the baseline's `overline` in all but the weight step. So the shared choice
|
|
47
|
-
is what the second consumer already made, and it satisfies `THEME-4` **without a baseline change**.
|
|
48
|
-
|
|
49
|
-
### Token delta list
|
|
50
|
-
|
|
51
|
-
**One entry** — the rest is drawn from the baseline's own tokens, so there is nothing else to reconcile.
|
|
52
|
-
|
|
53
|
-
- **`caveat` variant text: 12/500.** The baseline's nearest variant is `caption` at 12/**400**; there is
|
|
54
|
-
no 12/500. **Resolved by the operator as: keep the prototype value, do not add a baseline variant in
|
|
55
|
-
this WO.** It will surface as a `THEME-4` finding on the promoted component and must be **exempted
|
|
56
|
-
with that reason stated** — not silently flattened to 400, and not fixed by extending the baseline
|
|
57
|
-
here. Adding a variant is an app-level decision that does not belong inside a component promotion.
|
|
58
|
-
|
|
59
|
-
### Scope
|
|
60
|
-
|
|
61
|
-
1. **`StatTile`** as a kit component, per the decisions above. Props: `label`, `value`, `caption`,
|
|
62
|
-
`accent`, `children`, and a `minWidth` floor with a sensible default.
|
|
63
|
-
2. **`SoftChip`** as a kit component. Props: `label`, `tone` (`success`/`warning`/`error`/`info`),
|
|
64
|
-
`variant` (`caveat`/`status`), `icon` (defaulting to the 6 px dot), a border toggle, and `title` for
|
|
65
|
-
the tooltip.
|
|
66
|
-
3. **Colour derivation stays as authored** — fill `alpha(main, .12)`, border `alpha(main, .30)`, text and
|
|
67
|
-
dot `darken(main, .3)`, read from `theme.palette[tone]`. **Do not restate any hex.** This is the
|
|
68
|
-
property that makes the component exemplary and the reason its four tones all pass AA (measured in the
|
|
69
|
-
prototype: 7.11 / 7.33 / 7.67 / 9.20).
|
|
70
|
-
4. **Keep `enterTouchDelay={0}`** on the tooltip. It is why SoftChip's `title` is reachable on a touch
|
|
71
|
-
pointer — the exact failure `AUTH-4` is blocked on elsewhere. Do not drop it as boilerplate.
|
|
72
|
-
5. Exports from the package root, and the README line.
|
|
73
|
-
|
|
74
|
-
### Non-goals / do not touch
|
|
75
|
-
|
|
76
|
-
- **The three app adoptions.** hram, fitness-monitor and cockpit each get their own small follow-up WO.
|
|
77
|
-
This WO ships the kit component and nothing in any app.
|
|
78
|
-
- **No baseline/theme change.** No new typography variant, no palette edit. See the token delta list.
|
|
79
|
-
- **The other primitives in hram's `ResearchComponents/`** — `CardTitle`, `InfoPopover`,
|
|
80
|
-
`ProvisionalChip`, `FieldInfo`. `ProvisionalChip` wraps `SoftChip` and is the obvious next candidate,
|
|
81
|
-
but it has one build and is out of scope here.
|
|
82
|
-
- **`SoftChip`'s `caveat` weight.** Left at 12/500 deliberately.
|
|
83
|
-
- Any chart wrapper, shell component, or existing kit surface.
|
|
84
|
-
|
|
85
|
-
### Risks
|
|
86
|
-
|
|
87
|
-
- **Two apps' visible appearance changes when they adopt** — hram's status chips become uppercase and its
|
|
88
|
-
KPI values grow 16 → 18; cockpit's pills lose a weight step. All three are decided, not accidental, and
|
|
89
|
-
all three land in the *adoption* WOs rather than here. Worth stating in each so nobody reports them as
|
|
90
|
-
regressions.
|
|
91
|
-
- **Neither `StatTile` build has a test.** The promoted component's tests are written here from scratch,
|
|
92
|
-
not ported. `SoftChip` has `SoftChip.test.jsx` in hram — read it, but do not assume it covers the
|
|
93
|
-
promoted API.
|
|
94
|
-
- **`THEME-4` will flag the `caveat` 12/500.** Expected. Exempt with the reason; an unexplained exemption
|
|
95
|
-
is itself a finding by that check's own design.
|
|
96
|
-
- **Version:** this is a new capability area (two new components), so a **minor** — `2.38.0` → `2.39.0`.
|
|
97
|
-
|
|
98
|
-
### Required tests to WRITE
|
|
99
|
-
|
|
100
|
-
- **`StatTile`**: renders label/value/caption; omits the value row when `value` is undefined; renders
|
|
101
|
-
`children`; `accent` applies the 2 px border and the default does not; the value carries
|
|
102
|
-
`tabular-nums`.
|
|
103
|
-
- **`SoftChip`**: each of the four tones derives fill/border/text from `theme.palette[tone]` rather than a
|
|
104
|
-
literal (assert against the theme object, not against a hex string); `status` vs `caveat` differ in
|
|
105
|
-
radius and typography; the default leading element is the dot and a passed `icon` replaces it; the
|
|
106
|
-
border toggle works; `title` renders a tooltip and `enterTouchDelay` is 0.
|
|
107
|
-
- **Do not write a contrast assertion using `getContrastRatio`** — it ignores alpha, so on a 12 % tint it
|
|
108
|
-
answers a different question and would pass while meaning nothing. The prototype computes the real
|
|
109
|
-
ratios; if a contrast test is wanted it must blend the fill first.
|
|
110
|
-
|
|
111
|
-
Scope the run to these two new specs plus anything the new exports touch. Not the full suite — no
|
|
112
|
-
dependency change, no theme change.
|
|
113
|
-
|
|
114
|
-
### Parity guardrail
|
|
115
|
-
|
|
116
|
-
Visual and API only. The promoted components must be capable of rendering **both** existing builds'
|
|
117
|
-
appearances where a decision preserved them, and exactly the decided appearance where it did not. No
|
|
118
|
-
behaviour, permission or data-contract surface exists here.
|
|
119
|
-
|
|
120
|
-
---
|
|
121
|
-
|
|
122
|
-
## B. Implementation map
|
|
123
|
-
|
|
124
|
-
*Filled by the Orchestrator on `git pull` — see `AGENTS.md` → "Work Order".*
|
|
125
|
-
|
|
126
|
-
---
|
|
127
|
-
|
|
128
|
-
## C. Orchestrator only
|
|
129
|
-
|
|
130
|
-
> **STOP — if you are the implementer reading this work order as your own specification, this section is
|
|
131
|
-
> NOT addressed to you. Skip it entirely.** It tells the Orchestrator how to invoke you, how the result
|
|
132
|
-
> is reviewed, and how it is committed. **You ARE that invocation — do NOT shell out to `codex exec`, do
|
|
133
|
-
> NOT spawn reviewers, do NOT run tests beyond your own new ones, do NOT edit `WORK_ORDERS.md`, and do
|
|
134
|
-
> NOT `git add`/`commit`/`push`.** Leave the diff in the working tree.
|
|
135
|
-
|
|
136
|
-
### Execution directive
|
|
137
|
-
|
|
138
|
-
Check `.claude/codex-status.md` first. **No line for the current date means use Codex.** A line recording
|
|
139
|
-
`unavailable` for the current date means skip the attempt, implement directly in Claude, and name the
|
|
140
|
-
record and its date — that flips authorship. If the newest confirmation is older than today, make exactly
|
|
141
|
-
one probe and write the outcome back either way.
|
|
142
|
-
|
|
143
|
-
### Review routing
|
|
144
|
-
|
|
145
|
-
Tier 3: **independent `reviewer`** and **`ui_reviewer`**, concurrent, in one background batch, before the
|
|
146
|
-
commit. **No `sec_reviewer`** — no auth, permission, money or schema surface.
|
|
147
|
-
|
|
148
|
-
Ask `ui_reviewer` specifically whether the `overline` decision reads correctly at both variants, and
|
|
149
|
-
whether the `caveat` exemption is stated rather than assumed.
|
|
150
|
-
|
|
151
|
-
### Verification
|
|
152
|
-
|
|
153
|
-
**A prototype is in scope, so the rendered side-by-side at 375 px and 1280 px is a commit gate** — see
|
|
154
|
-
`AGENTS.md` → Reviews. Note the known constraint: the Browser pane composites only while displayed and
|
|
155
|
-
screenshots have not been legibly obtainable in this repo. If capture fails, **declare the substitution
|
|
156
|
-
in the register Notiz** and say what was inspected instead (computed styles, DOM) — an undeclared
|
|
157
|
-
substitution is a skipped gate, and that fallback cannot see whether a screen reads heavy or dense.
|
|
158
|
-
|
|
159
|
-
The prototype's own contrast numbers are computed at render time; re-read them rather than restating
|
|
160
|
-
these.
|
|
161
|
-
|
|
162
|
-
### Register + commit
|
|
163
|
-
|
|
164
|
-
One row, `PRIM-1`, with the `PRIM-*` prefix documented in the register header. `done` only with both
|
|
165
|
-
reviewers and their verdicts named, plus the rendered-gate evidence or its declared substitution.
|
|
166
|
-
|
|
167
|
-
Publishing is a separate step from committing: **a landed commit is not a publish.** If the three
|
|
168
|
-
adoption WOs are to follow, `2.39.0` must actually be on the registry first — verify against npm, not
|
|
169
|
-
against a commit.
|
|
170
|
-
|
|
171
|
-
### Mini-handover
|
|
172
|
-
|
|
173
|
-
Repo: `ui-core-micha` (`C:\Users\biglmi\Documents\webapps\ui-core-micha`), branch `main`.
|
|
174
|
-
WO: `work-orders/PRIM-1.md`. Promotes two components whose second builds are inline in
|
|
175
|
-
`fitness-monitor/BodyHistoryPage.jsx:554` and `cockpit/BoardView.jsx:65`. Seven divergences already
|
|
176
|
-
decided — the table in Part A is the spec, the prototype renders it. Tier 3, both reviewers, rendered
|
|
177
|
-
gate applies, minor version `2.39.0`. App adoptions are separate WOs. Follow `orchestrate-codex`.
|