@micha.bigler/ui-core-micha 3.11.1 → 3.11.2

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.11.2 — UCM-THEME-16
4
+
5
+ The "Signup Access / valid until" box in `QrSignupManager` now takes `background.subtle` instead of the fixed light `grey.50`,
6
+ so its text stays readable in dark mode (2.00:1 before). In light the ground moves from `#FAFAFA` to `#F4F5F6` (1.05:1 step).
7
+ The white ground behind the QR code itself is unchanged on purpose.
8
+
3
9
  ## 3.11.1 — UCM-I18N-5
4
10
 
5
11
  `UserListComponent`'s pagination range text ("1–4 of 4") now goes through `labelDisplayedRows`,
package/WORK_ORDERS.md CHANGED
@@ -38,7 +38,7 @@ prefixed ID; never reuse a bare flat number across workstreams.
38
38
  | UCM-MSG-20 | [Message composer collapses on narrow screens](work-orders/UCM-MSG-20.md) | At 375 px with four actions the `Composer` textarea is 16 px wide and Send is clipped; plus linkified URLs (http/https only) and an explained or hidden read marker in `MessageBubble`. | 2026-09-29 | done | fcd4ae6 | Raised from jg-ferien's local browser test 2026-09-29; operator 2026-09-29: generic fixes go to ucm. From jg `JG-UX-14`. dcm register is canonical for `MSG-*`; this is the ucm-side row. Tier 3 (inside shared-core). Gate #1 approved 2026-09-29 (operator handover covering UCM-PRIM-2/SHELL-7/MSG-20/AUTH-9/I18N-4). Codex implemented (available today, no fallback): `Composer`'s action row split into an icon row + a message-field/send row (`sx={{flex:1,minWidth:120}}`/`sx={{flexShrink:0}}`); new `linkifyText.jsx` (strict `https?://` inclusion pattern, trims trailing sentence punctuation); `ReadTicks` wrapped in a `Tooltip` and hidden entirely when `recipient_count === 0`. Independent `reviewer` (all 4 lenses) + `ui_reviewer` + `sec_reviewer` (Codex gpt-5.6-luna, concurrent background batch, full diff): **review: codex/gpt-5.6-luna · lenses: envelope,regression,duplication,tests,ui,sec · 6 raised · 3 accepted · worst accepted: defect** — the composer test only asserted CSS values, not that the actions actually moved to a separate DOM row (a same-row implementation with the same sx values would have passed); the read-marker/linkify test suite lacked a structural row-separation proof; and `linkifyText`'s raw `<a>` diverged from the kit's own `Link` primitive already used elsewhere (`CookieConsentStep.jsx`). Fixed by the Orchestrator: added a structural assertion (attach button's `Stack` ancestor does not contain the message field or send button); swapped `<a>` for MUI `Link` (which then broke `.closest('.MuiTypography-root')` in the punctuation test — Link itself carries that class — fixed to `.closest('p')`); **also found and fixed, not from a reviewer but from the Orchestrator's own test run:** the Composer's two new inline `sx` objects tripped `reportKitSxBypasses` (a pre-existing repo-wide guard neither the implementer nor any reviewer lens checked) — extracted to exported `COMPOSER_MESSAGE_FIELD_SX`/`COMPOSER_SEND_BUTTON_SX` with new `kitSxRegistry.js` entries. Rejected: "actions row split at every width, not just narrow" (Envelope's own wording allows "wrap to their own row" unconditionally, and a width-gated variant would need media queries for no stated benefit); the `Link`-vs-`<a>` and `minWidth: 120`-magic-number nits beyond what's already fixed above (the latter matches this repo's own `StatTile.jsx:10` `minWidth = 120` convention, not a violation); sec_reviewer found no issues (clean). Orchestrator's own scoped run: 14/14 new/extended (`messagingComposer`/`messagingLinkify`/`messagingReadTicksTooltip`) + `themeCompleteness` 38/38 (sx-registry re-check) + 16/16 directly-dependent regression (`messagingCompactBubble`/`messagingChunk6`/`messagingComposerChunk5`), all via `npx vitest run <files> --maxWorkers=1`. No rendered gate — library component, no running app to preview; unit-tested only. **Released as `3.7.0`** together with `PRIM-2`/`SHELL-7`/`AUTH-9`/`I18N-4` in one combined release — see `I18N-4`'s row for the release note; jg-ferien's pin bump is that app's own separate step. |
39
39
  | UCM-SHELL-7 | [User-menu avatar initials fail contrast](work-orders/UCM-SHELL-7.md) | `UserMenu` avatar renders MUI default white-on-grey initials at 1.83:1; take colours from theme tokens with a passing fallback. | 2026-09-29 | done | 04a29fe | Raised from jg-ferien's local browser test 2026-09-29; operator 2026-09-29: generic fixes go to ucm. From jg `JG-DL-12`. Tier 3 (inside shared-core). Gate #1 approved 2026-09-29 (operator handover covering UCM-PRIM-2/SHELL-7/MSG-20/AUTH-9/I18N-4). Codex implemented (available today, no fallback): avatar background = `primary.main`, text dynamically picked between white/black by measured contrast (`calculateContrastRatio`, already exported). Independent `ui_reviewer` only (Codex gpt-5.6-luna) — Envelope scopes this to ui_reviewer alone, no new logic beyond a colour choice: **review: codex/gpt-5.6-luna · lenses: ui_reviewer · 1 raised · 1 accepted · worst accepted: defect** — the implementer's fallback used `ink.primary` (#212529, luminance ~0.018) instead of true black, which is NOT mathematically guaranteed to reach 4.5:1 together with `background.paper`: for a `primary.main` in the ~0.183–0.257 luminance band neither branch cleared 4.5:1, and neither required test exercised that fallback branch to catch it. Fixed by the Orchestrator: switched both branches to `theme.palette.common.white`/`.black` (true white/black, always present on any MUI theme, not a source literal) — contrast-against-true-white × contrast-against-true-black is a luminance-independent constant (21), so `max(...) >= sqrt(21) ~= 4.58` holds for every possible `primary.main`, closing the gap rather than narrowing it. Added a third test with a synthetic mid-grey `primary.main` (`#808080`, ~3.95:1 on white) to actually exercise the black-fallback branch. Orchestrator's own scoped run: `tests/UserMenu.test.jsx` 10/10 green (`npx vitest run tests/UserMenu.test.jsx --maxWorkers=1`). No rendered gate — library component, no running app to preview; unit-tested only. **Released as `3.7.0`** together with `PRIM-2`/`MSG-20`/`AUTH-9`/`I18N-4` in one combined release — see `I18N-4`'s row for the release note; jg-ferien's pin bump is that app's own separate step. |
40
40
  | UCM-PRIM-2 | [Shared EmptyState primitive](work-orders/UCM-PRIM-2.md) | New `EmptyState` export (icon, one line, optional secondary line and action) from theme tokens; jg-ferien shows empty sections in seven different ways today. | 2026-09-29 | done | 059e9f0 | Raised from jg-ferien's local browser test 2026-09-29; operator 2026-09-29: generic fixes go to ucm. Adopted in jg `JG-UX-9`. Tier 3 (new public export, additive). Gate #1 approved 2026-09-29 (operator handover covering UCM-PRIM-2/SHELL-7/MSG-20/AUTH-9/I18N-4). Codex implemented (available today, no fallback). Independent `reviewer` (Codex gpt-5.6-luna, all 4 lenses) + `ui_reviewer` (Codex gpt-5.6-luna), concurrent background batch on the full diff: **review: codex/gpt-5.6-luna · lenses: envelope,regression,duplication,tests,ui · 5 raised · 3 accepted · worst accepted: defect** — missing README doc entry (Envelope required one, added), action button missing `type="button"` (form-submit risk, fixed), two test assertions passed for a would-be hardcoded stub (title/description always rendered the same fixed string across all 3 tests; strengthened with a second distinct-value render + negative assertion). Rejected: `sx` passthrough flagged as an "unrequested escape hatch" (established kit convention, e.g. `Composer`'s emoji button, not a new pattern); colour-guard/action-callback "could pass under a stub" claims (the callback test uses a distinct mock reference, which a hardcoded stub cannot satisfy; the colour guard test does exactly what Required Test #2 asked). Orchestrator's own scoped run: `tests/EmptyState.test.jsx` 4/4 green, `tests/themeCompleteness.test.js` (directly-dependent, directory-enumerated palette scan) 38/38 green. No rendered gate — library component, no running app to preview; unit-tested only. **Released as `3.7.0`** together with `SHELL-7`/`MSG-20`/`AUTH-9`/`I18N-4` in one combined release — see `I18N-4`'s row for the release note. |
41
- | UCM-THEME-16 | Dark-safe tokens for QrSignupManager and ReadTicks | `QrSignupManager.jsx` uses `grey.50` and `ReadTicks.jsx` uses `grey.500`, both fixed light-ish MUI values that do not follow the dark baseline. Swapping to `background.subtle`/`text.disabled` changes the light value (`#FAFAFA`->`#F4F5F6`, `#9E9E9E`->`#6A7178`), so it needs an operator decision on accepting that light change. Raised by UCM-THEME-14 review | 2026-10-02 | planned | | Tier 2 - decision needed: accept the small light change, or add a token |
41
+ | UCM-THEME-16 | Dark-safe info box in `QrSignupManager` | `QrSignupManager.jsx:376` info box `grey.50` -> `background.subtle` (dark text was 2.00:1 on the fixed light ground). Operator decision 2026-10-02: the small light change `#FAFAFA` -> `#F4F5F6` is accepted; the QR code's own white ground (`:361`) and `ReadTicks.jsx` `grey.500` (6.07:1 in dark) stay unchanged on purpose. Full order [`work-orders/UCM-THEME-16.md`](work-orders/UCM-THEME-16.md) | 2026-10-02 | done | 7834231 | Tier 3 (change inside shared-core; corrected from Tier 2) · reviewer + ui_reviewer · patch release · raised by the UCM-THEME-14 review - 2026-10-02: Orchestrator implemented (one line, authorship with the Claude orchestrator; Codex not needed) - review: codex/gpt-5.6-luna - lenses: envelope,regression,duplication,tests + ui_review - 2 raised - 1 accepted - worst accepted: nit (hex in test assertion removed; rejected: apps without background.subtle, MFAComponent already depends on it) - release 3.11.2 - test fails on revert to grey.50 (mutation-checked) - no rendered side-by-side (no prototype; style assertion in both modes) |
42
42
  | UCM-THEME-14 | Dark mode in `createAppTheme`, from the central token file | `palette.mode: 'dark'` selects a dark baseline palette in `tokens.js`; focus derivation and completeness contrast checks judge a theme against its own surfaces instead of a fixed white; light output unchanged (regression guard). Full order [`work-orders/UCM-THEME-14.md`](work-orders/UCM-THEME-14.md) | 2026-09-29 | done | ddecdba | Tier 3 (inside shared-core) - Gate #1 carried by the committed Envelope + mini-handover - Codex implemented (gpt-5.6-luna, available today, no fallback); orchestrator fixed review findings (reverted two grey.* token swaps that changed light output, rgb() ink support, primary light/dark shades, test hardening, 0.02 alpha step to reproduce the frozen 0.38/0.53) - review: codex/gpt-5.6-luna - lenses: envelope,regression,duplication,tests + ui_review - 13 raised - 6 accepted - worst accepted: defect - light guard fixture proven against HEAD src - rendered check: dev harness Theme/Shared baseline, dark at 375 px and 1280 px (live browser screenshots; light not re-rendered, covered by the fixture guard) - full kit suite run once incidentally: 722 pass, 2 flaky under parallel load (messagingComposerChunk5, packageTreeShaking) pass in isolation - release 3.11.0 publishes on push - downstream: webshop-guenter pin bump planned
43
43
  | UCM-THEME-13 | Freeze a dark-mode baseline token set | Reference sheet `docs/DARK-TOKENS.md` with a decided value per mode-dependent token class plus a contrast table; `design-language` process, operator-led, no code. Full order [`work-orders/UCM-THEME-13.md`](work-orders/UCM-THEME-13.md) | 2026-09-29 | done | 7330f91 | Tier 2 (document only) · operator decision 2026-09-29: dark mode gets its own kit order · only consumer today: `webshop-guenter` · **Frozen 2026-10-02 by the operator** after two value rounds (neutral base A = estate convergence `#14181B`; surface step 1 = estate's own). Sheet `docs/DARK-TOKENS.md`, instrument `work-orders/assets/UCM-THEME-13-dark-prototype.html`. Invented and labelled: dark `info`, dark `stale`, overlay shadow, scrim. Document order: no implementer, no review (Envelope). |
44
44
  | AUTH-7 | `loginWithPassword`s 409-Retry gegen Zweitfehlschlag haerten | allauth-headless antwortet mit 409 ("already authenticated"), wenn die Session bereits gueltig ist; `loginWithPassword` ruft dann `fetchCurrentUser()` erneut auf. Schlaegt dieser Retry ebenfalls fehl, wurde der Fehler bisher roh durchgereicht statt normalisiert. Spec [`work-orders/AUTH-7.md`](work-orders/AUTH-7.md) | 2026-09-05 | done | ae7aa89 | **Tier 3** (Shared-Core). Ausgeloest durch `kira`: ein erschoepfter globaler Throttle-Bucket (siehe `kira/KIRA-TRACK-3`) liess den 409-Retry selbst mit 429 fehlschlagen, roh statt normalisiert durchgereicht. Rein additiver Bugfix, kein Prop-/Verhaltensvertrag geaendert. **Implementiert (Codex gpt-5.6-luna):** genau wie geplant, `src/auth/authApi.jsx` + neue `tests/authApiLogin.test.js`. review: codex/gpt-5.6-luna (`reviewer` 4 Lenses + `ui_reviewer` + `sec_reviewer`, ein Hintergrund-Batch) 3 raised · 1 accepted · 2 rejected · worst accepted: defect (aber ausserhalb des Scopes dieser WO, siehe unten) — `envelope`/`regression`/`duplication`/`sec_reviewer`: 0 raised. `tests`-Lens: 2 raised, BEIDE empirisch widerlegt (Fix testweise zurueckgerollt, betroffener Test schlug korrekt fehl — sind keine echten Luecken) → rejected. `ui_reviewer`: 1 raised, akzeptiert als real (empirisch bestaetigt: `normaliseApiError('...', 'Auth.LOGIN_FAILED')` liefert bei einem DRF-`{"detail": "..."}`-Fehler `code: 'GENERIC'` statt des übergebenen Default-Codes, weil `extractErrorInfo` in `src/utils/auth-errors.js` `'GENERIC'` bereits als gesetzten Wert zurückgibt, der `info.code || defaultCode` gewinnt) — **PRE-EXISTING in einer anderen Datei, nicht durch diesen Diff verursacht, an JEDER anderen Call-Site derselben Funktion bereits vorhanden** → out-of-scope fuer AUTH-7, nicht hier gefixt, nachverfolgt als `AUTH-8`. Tests: `npx vitest run tests/authApiLogin.test.js tests/AuthContext.test.jsx tests/authApiRegistration.test.js` 7/7 gruen. **PATCH-Bump 3.5.0 -> 3.5.1** (`package.json` + `CHANGELOG.md`), kein Consumer-Pin-Bump noetig (rein additiv). |
@@ -289,7 +289,7 @@ export function QrSignupManager({ enabled = false, expiryDays = DEFAULT_EXPIRY_D
289
289
  mt: 2,
290
290
  borderRadius: 3,
291
291
  p: 2,
292
- bgcolor: 'grey.50',
292
+ bgcolor: 'background.subtle',
293
293
  border: '1px solid',
294
294
  borderColor: 'divider',
295
295
  }, children: [_jsx(Typography, { variant: "subtitle2", gutterBottom: true, children: t('Auth.SIGNUP_QR_ACCESS_TITLE', 'Signup Access') }), _jsxs(Typography, { variant: "body2", color: "text.secondary", children: [t('Auth.SIGNUP_QR_VALID_UNTIL', 'Valid until'), ": ", formattedExpiry || result.expires_at] })] }), _jsxs(Stack, { direction: { xs: 'column', sm: 'row' }, spacing: 1, sx: { mt: 2 }, children: [_jsx(Button, { variant: "outlined", onClick: () => generate(), disabled: busy, children: t('Auth.SIGNUP_QR_NEW_BUTTON', 'New QR-Code') }), _jsx(Button, { variant: "outlined", onClick: handleCopyLink, disabled: !(result === null || result === void 0 ? void 0 : result.signup_url) || busy, children: t('Auth.SIGNUP_QR_COPY_BUTTON', 'Copy Link') }), _jsx(Button, { variant: "outlined", onClick: handleSavePdf, disabled: !(result === null || result === void 0 ? void 0 : result.signup_url) || busy, children: t('Auth.SIGNUP_QR_PDF_BUTTON', 'Save as PDF') })] })] }))] }));
@@ -47,7 +47,7 @@ stated rule from a decided value) or **invented** (no precedent anywhere; labell
47
47
 
48
48
  | Token | Light (today) | Dark | Origin |
49
49
  |---|---|---|---|
50
- | `controlBorder.main` | `rgba(33,37,41,.50)` | `rgba(230,235,239,.38)` | derived: `ink.primary` at the smallest alpha that clears 3:1 on page and paper |
50
+ | `controlBorder.main` | `rgba(33,37,41,.50)` | `rgba(230,235,239,.38)` | derived: `ink.primary` at the smallest alpha **in 0.02 steps from 0.30** that clears 3:1 on page and paper (wording made precise 2026-10-02; a finer step would give 0.37, the frozen value is 0.38) |
51
51
  | `controlBorder.hover` | `rgba(33,37,41,.65)` | `rgba(230,235,239,.53)` | derived: the light step (+0.15) carried over |
52
52
  | `controlBorder.error` | `#BF3227` | `#E58B80` | = `error.main` (dark) |
53
53
  | `controlBorder.focus` | derived from primary | derived from primary | rule: lighten the primary until it clears 3:1 on page and paper |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@micha.bigler/ui-core-micha",
3
- "version": "3.11.1",
3
+ "version": "3.11.2",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "sideEffects": [
@@ -373,7 +373,7 @@ export function QrSignupManager({
373
373
  mt: 2,
374
374
  borderRadius: 3,
375
375
  p: 2,
376
- bgcolor: 'grey.50',
376
+ bgcolor: 'background.subtle',
377
377
  border: '1px solid',
378
378
  borderColor: 'divider',
379
379
  }}
@@ -0,0 +1,49 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import React from 'react';
4
+ import { ThemeProvider } from '@mui/material/styles';
5
+ import { cleanup, render, screen } from '@testing-library/react';
6
+ import { afterEach, describe, expect, it, vi } from 'vitest';
7
+
8
+ vi.mock('../src/auth/authApi', () => ({
9
+ createSignupQr: vi.fn().mockResolvedValue({
10
+ signup_url: 'https://example.test/signup?rt=token',
11
+ expires_at: '2026-09-01T12:00:00Z',
12
+ }),
13
+ }));
14
+
15
+ vi.mock('qrcode.react', () => ({
16
+ QRCodeSVG: () => null,
17
+ }));
18
+
19
+ const stableT = (_key, fallback) => fallback;
20
+ const stableI18n = { exists: () => false, t: stableT };
21
+ vi.mock('react-i18next', () => ({
22
+ useTranslation: () => ({ t: stableT, i18n: stableI18n }),
23
+ }));
24
+
25
+ import { QrSignupManager } from '../src/components/QrSignupManager';
26
+ import { createAppTheme } from '../src/theme';
27
+
28
+ // jsdom serialises the resolved colour as rgb(), so compare against the theme's own value in that form.
29
+ function toRgb(hex) {
30
+ const [r, g, b] = [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16));
31
+ return `rgb(${r}, ${g}, ${b})`;
32
+ }
33
+
34
+ describe('QrSignupManager info box', () => {
35
+ afterEach(cleanup);
36
+
37
+ it.each(['light', 'dark'])('paints background.subtle of the %s theme', async (mode) => {
38
+ const theme = createAppTheme({ palette: { mode, primary: { main: '#468AB2' } } });
39
+ render(
40
+ <ThemeProvider theme={theme}>
41
+ <QrSignupManager enabled expiryDays={90} />
42
+ </ThemeProvider>,
43
+ );
44
+
45
+ const title = await screen.findByText('Signup Access');
46
+ const box = title.parentElement;
47
+ expect(getComputedStyle(box).backgroundColor).toBe(toRgb(theme.palette.background.subtle));
48
+ });
49
+ });
@@ -0,0 +1,80 @@
1
+ # UCM-THEME-16: Dark-safe info box in `QrSignupManager`
2
+
3
+ # A. Envelope, authored by the Expertenchat
4
+
5
+ ## Goal & expected outcome
6
+
7
+ The "Signup Access / valid until" box under a generated signup QR code stays readable in dark mode. Today it
8
+ paints a fixed light ground, `grey.50` (`#FAFAFA`), under the theme's secondary text; in dark that is
9
+ `#AAB5BF` on `#FAFAFA`, **2.00:1**. After this order the box takes `background.subtle`, which resolves per
10
+ mode: light `#F4F5F6` (5.37:1 for its text), dark `#20262C` (7.32:1).
11
+
12
+ ## Decided (operator, 2026-10-02)
13
+
14
+ - **`src/components/QrSignupManager.jsx:376`**: `bgcolor: 'grey.50'` -> `bgcolor: 'background.subtle'`.
15
+ **The light change is accepted**: `#FAFAFA` -> `#F4F5F6`, a 1.05:1 step, barely visible.
16
+ - **`src/components/QrSignupManager.jsx:361`** (`bgcolor: '#ffffff'` behind the QR code itself) **stays white
17
+ on purpose**: a QR code needs a light quiet zone to scan. Recorded in `docs/DARK-TOKENS.md`.
18
+ - **`src/messaging/ReadTicks.jsx:14`** (`DM_SENT_COLOR = 'grey.500'`) **stays unchanged.** It already clears
19
+ contrast in dark (6.07:1 on the dark paper). The candidate `text.disabled` (`#6A7178`) is the value the file's
20
+ own comment records as rejected, because it read too close to the "read" state. In light the tick sits at
21
+ 2.68:1 on white. That is a pre-existing condition, softened by the icon shape (Done vs DoneAll), and not
22
+ part of this order.
23
+
24
+ These decisions replace the register row's original question ("accept the small light change, or add a
25
+ token"), raised by the `UCM-THEME-14` review.
26
+
27
+ ## Scope + non-goals
28
+
29
+ In scope: the one property at `QrSignupManager.jsx:376`; `CHANGELOG.md`; a **patch** release.
30
+
31
+ Non-goals: no change to `ReadTicks.jsx`, to the QR code's white ground, or to any token value.
32
+
33
+ ## Tier · precondition / gate
34
+
35
+ - **Tier 3 · tests: one rendered-style assertion per mode for the info box; the component's existing tests.**
36
+ A change inside shared-core, even at one line (`AGENTS.md` -> Tiering).
37
+ - Precondition: `UCM-THEME-14` landed (3.11.0, confirmed 2026-10-02).
38
+
39
+ ## Risks
40
+
41
+ - The light change is the only light-output change since `UCM-THEME-14`'s regression fixture. If that fixture
42
+ covers this component, it needs this one entry updated, named, and nothing else.
43
+
44
+ ## Required tests to WRITE (you write them and run YOUR OWN new ones; the Orchestrator's run is the gate)
45
+
46
+ 1. In a light theme from `createAppTheme`, the info box's background resolves to `#F4F5F6`; in a dark theme to
47
+ `#20262C`. Assert against the theme's own `background.subtle`, not a copied hex.
48
+
49
+ Run, do not write: the existing `QrSignupManager` tests and the light regression fixture.
50
+
51
+ ## Parity guardrail
52
+
53
+ No behaviour change. One background, both modes.
54
+
55
+ ---
56
+
57
+ # B. Implementation map, filled by the Orchestrator and ADDRESSED TO THE IMPLEMENTER
58
+
59
+ *Placeholder. The Orchestrator fills the context package, the absolute working directory, the
60
+ progress contract and the preamble block on `git pull`, per `AGENTS.md` -> "Work Order". A one-line change
61
+ may be implemented by the Orchestrator directly; authorship then sits with it and the independent review
62
+ carries independence. Do not dispatch while this placeholder stands.*
63
+
64
+ ---
65
+
66
+ # C. Orchestrator only, NOT ADDRESSED TO THE IMPLEMENTER
67
+
68
+ > **If you are the implementer reading this work order as your own specification: STOP at this line.**
69
+ > Everything below describes what the Orchestrator does AFTER you finish. You do none of it: no
70
+ > reviewers, no verification run, no register edit, no `git add`/`commit`/`push`.
71
+
72
+ - Check `.claude/codex-status.md` first (newest-first). Before `git push`, run `git log origin/main..HEAD` and
73
+ push only if every listed commit is this order's.
74
+ - Review: Tier 3, independent `reviewer` (all configured lenses) and `ui_reviewer`, one batch, before the
75
+ commit.
76
+ - Release: verify the publish through the job log (`npm view` lags for provenance publishes). Row -> `done`
77
+ with the review Notiz and the version.
78
+
79
+ Mini-handover: `Orchestrator: implement work-orders/UCM-THEME-16.md in ui-core-micha (main). git pull first,
80
+ read the WO, then follow orchestrate-codex.`