@micha.bigler/ui-core-micha 2.35.0 → 2.37.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.
@@ -0,0 +1,183 @@
1
+ # AUTH-4 — The account user list on a phone
2
+
3
+ **Target repo:** `ui-core-micha` (branch `main` — this repo has no `develop`)
4
+ **Tier:** 3 (shared core)
5
+ **Review:** independent `reviewer` **and** `ui_reviewer` — both mandatory, concurrent
6
+ **Version target:** the next **minor** after `SHELL-5` — the component gains a responsive mode it did
7
+ not have, which is a capability rather than a fix. Read the published version at implementation time.
8
+ **Prototype:** `work-orders/assets/AUTH-4-user-list-card.html` — the **composition** spec. The
9
+ two-width rendered side-by-side is therefore a commit gate.
10
+ **Sequencing:** **after `SHELL-5`.** Both are ucm work orders on the same screen; `SHELL-5` changes how
11
+ the users section is *reached*, this changes what is *in* it.
12
+ **Strand:** split out of `DS-11` by operator decision, because its fix class differs from the
13
+ navigation shell's.
14
+
15
+ ---
16
+
17
+ ## A. Envelope
18
+
19
+ ### Goal
20
+
21
+ `UserListComponent`'s table is right for a desktop and unusable on a phone. Give it a card layout
22
+ below `md`. Above `md` **nothing changes**.
23
+
24
+ ### The driver, measured 2026-08-11 in cockpit
25
+
26
+ All figures at `innerWidth` **411** CSS px — the mobile preset yields 411, not 375, so at the real
27
+ target every figure is worse.
28
+
29
+ | | measured |
30
+ |---|---|
31
+ | Table width | **1053 px** |
32
+ | Container | **325 px** → 728 px of overflow |
33
+ | Container `overflow-x` | `auto`; **the document does not overflow** |
34
+ | Columns | Email 220, Name 180, New 90, Successful Login 163, Role 180, Actions 220 |
35
+ | Cell padding | `10px 16px` — already the baseline's `density.tableCellPadding` |
36
+
37
+ **This is not a broken layout.** The container scrolls and the page does not. It is a desktop table
38
+ shown unchanged on a phone, with Role and Actions — the two things the screen exists for — entirely
39
+ outside the view.
40
+
41
+ **The cheap fix was tested and does not work.** 253 px of that width is `New` (90) and
42
+ `Successful Login` (163), both **default-on props an app can switch off today** —
43
+ `AccountPage` forwards all of them (`AccountPage.jsx:238-240`), and cockpit simply left the defaults.
44
+ But the four remaining columns are Email 220 + Name 180 + Role 180 + Actions 220 = **800 px** against
45
+ a 325 px container. **Configuration cannot fix this; only structure can.** That is now established
46
+ rather than assumed.
47
+
48
+ `SHELL-2` measured 1045 px on 2026-08-08 and it is 1053 px today (+8 px), so the number is stable —
49
+ `THEME-4`/`THEME-5` did not move the rendered density.
50
+
51
+ ### Why cards and not a scrolling table
52
+
53
+ The component is **interactive per row**: the role is an inline `Select`
54
+ (`UserListComponent.jsx:358-364`), there is a delete action plus app-supplied `extraRowActions`
55
+ (`:425-451`), a search field (`:506`), sortable columns (`:541`) and pagination. An administrator
56
+ opens this screen to **change a role** or **remove someone**. Any layout that puts those behind
57
+ horizontal scrolling defeats the screen's purpose — which is why a sticky-first-column table was
58
+ considered and rejected: it makes the defect tolerable instead of fixing it.
59
+
60
+ ### Scope
61
+
62
+ Below `md`, one card per user. The prototype is the composition spec; **four decisions in it are
63
+ load-bearing and must not be quietly re-decided:**
64
+
65
+ 1. **The email is the card title, the name is secondary.** The email is what an administrator
66
+ searches by and the only guaranteed-present field. A user without a name gets a **muted
67
+ placeholder**, not an empty line. Verified in the prototype: a 47-character address wraps to two
68
+ lines via `overflow-wrap: anywhere` without overflowing a 357 px card.
69
+ 2. **The two boolean columns become labelled chips.** In the table they are icon-only with a
70
+ `Tooltip` (`:187-195`) — and **a tooltip is unreachable on a touch pointer**, so the information is
71
+ effectively absent on the device this work order is about. Chips carry the words. This is a
72
+ deliberate small improvement over strict parity, not drift, and it is why the flags cost vertical
73
+ space instead of being dropped.
74
+ 3. **Role and delete sit in a footer behind a divider.** They are the actions; separating them keeps
75
+ the card's top half purely identifying. The select fills the remaining width so its value is never
76
+ truncated; delete keeps the full 44 px touch target.
77
+ 4. **Sorting needs its own control.** A card list has no column headers to click, so the
78
+ sortable-column affordance **disappears with the table**. A compact sort control sits beside the
79
+ search field. Without it the mobile view silently loses a capability the desktop has — the kind of
80
+ regression that is invisible in a diff and that no test would catch unless it is asked for. It is
81
+ asked for here.
82
+
83
+ **What the app supplies must keep working**, since the column set is app-configurable:
84
+
85
+ | Input | In the card |
86
+ |---|---|
87
+ | `extraColumns` | rendered as **label: value** rows below the chips, in the given order. **No new required field** on the column definition — an app that passes columns today keeps working unchanged. |
88
+ | `extraRowActions` | join delete in the footer. **Beyond three actions total they must collapse into an overflow menu**, or the footer wraps and the card loses its shape. |
89
+ | `showNewColumn` / `showSuccessfulLoginColumn` / `showRoleColumn` | chips (or the role select) when on, **absent** when off — a shorter card, not an empty chip row. |
90
+
91
+ ### Non-goals / do not touch
92
+
93
+ - **The table above `md`.** Byte-identical. Six columns are scannable at 1136 px and the strip beside
94
+ them has 570 px of headroom.
95
+ - **The column defaults.** `New` and `Successful Login` stay default-on. Flipping them would be a
96
+ silent change to the account screen in every app — a separate decision, not a side effect of this
97
+ work order.
98
+ - **The delete confirmation dialog**, the invite flow, the role list an app supplies, and every other
99
+ account section.
100
+ - **The section navigation** — that is `SHELL-5`.
101
+ - No permission, data-contract or API change. No new required prop. No baseline token change: the
102
+ card consumes tokens and introduces none, so there is **no token delta list**.
103
+ - No pagination or search redesign; both stay, above the list.
104
+
105
+ ### Risks
106
+
107
+ - **`AccountPage` is mounted by at least ten apps** — bigler-consult, cockpit, fitness-monitor,
108
+ hpc-bridge, hram, innoservice, jg-ferien, kerzenziehen, reimbursements. The account screen's user
109
+ list changes on a phone everywhere at once, and there is no staging net: a version bump on `main`
110
+ publishes. The two reviews and the rendered gate carry it.
111
+ - **Two renderings to keep in step.** From now on a change to a column must be made twice, or the
112
+ card and the table drift. Tests 3 and 4 below are what make the drift visible.
113
+ - **A long list of cards is a long scroll.** Mitigated by the existing search and pagination, not by
114
+ this work order — and worth watching in an app with many users rather than asserting it is fine.
115
+ - **`extraRowActions` beyond three** is the shape's real limit. The overflow-menu rule is in scope
116
+ precisely so the limit is handled rather than discovered.
117
+
118
+ ### Required tests to WRITE
119
+
120
+ 1. Below `md` the card list renders and no `<table>` is present; at `md` and above the table renders
121
+ and no cards are. **Prove both directions** — a one-sided test passes on a component that never
122
+ switches.
123
+ 2. A user without a name renders the muted placeholder, not an empty element.
124
+ 3. `extraColumns` appear as label/value rows in the card, in the given order, and still as columns in
125
+ the table.
126
+ 4. `extraRowActions`: three or fewer render inline in the footer; **more than three collapse into an
127
+ overflow menu**.
128
+ 5. Each of the three column toggles set to `false` removes its chip from the card **and** its column
129
+ from the table — no empty chip row.
130
+ 6. The mobile sort control changes the order, and the sortable column headers still work above `md`.
131
+ 7. Pagination and search still function in the card mode.
132
+ 8. `THEME-4`'s shadowing check still returns no findings — any `sx` the card introduces must not
133
+ duplicate a baseline `styleOverrides` key. The card uses `MuiCard`, which the baseline styles, so
134
+ this is a live risk rather than a formality.
135
+
136
+ Plus `tsc -p tsconfig.build.json --noEmit` clean. **No full-suite run** — affected set is
137
+ `UserListComponent`'s spec, `AccountPage`'s, and the theme spec touched by test 8.
138
+
139
+ ### Verification
140
+
141
+ The **two-width rendered side-by-side at 375 px and 1280 px** against the prototype, per the standing
142
+ gate. Measure, do not eyeball, the number this work order exists to fix: **at 375 px no element may
143
+ exceed its container**, and the 728 px of overflow must be gone. At 1280 px the table must be
144
+ unchanged — compare it against the prototype's section 3, which exists so that "unchanged" is part of
145
+ the spec rather than an omission.
146
+
147
+ State whether real screenshots were obtained or DOM/computed-style inspection substituted. Capture has
148
+ failed repeatedly in this estate; the substitution is declared, not silent.
149
+
150
+ ### Parity guardrail
151
+
152
+ Presentation only, with the one stated exception: the boolean flags gain visible labels because their
153
+ tooltip is unreachable on touch. Every datum the table shows, the card shows. No behaviour, no
154
+ permissions, no data contract, no routing.
155
+
156
+ ---
157
+
158
+ ## B. Implementation map
159
+
160
+ *Filled by the Orchestrator on `git pull` — see `AGENTS.md` → "Work Order".*
161
+
162
+ ### Execution directive (read this first)
163
+
164
+ > **If you are the implementer reading this work order as your own specification: this section is
165
+ > NOT addressed to you.** It tells the Orchestrator how to invoke you. **You ARE that invocation —
166
+ > do NOT shell out to `codex exec`.**
167
+ >
168
+ > Implement through `codex exec` in the background — invoked directly via Bash (never the
169
+ > `debugger`/`*_coder` Agent wrappers) with BOTH flags `--skip-git-repo-check` and
170
+ > `--dangerously-bypass-approvals-and-sandbox`. **Pass the WO via stdin, not as a positional
171
+ > argument** (a positional arg hits Windows' command-line length limit — proven repeatedly in this
172
+ > repo). Fallback to direct Claude implementation only on Codex quota/rate-limit/non-zero exit — and
173
+ > note that the fallback flips authorship, so both reviewers stay mandatory either way.
174
+ >
175
+ > **Read `git log origin/main..HEAD` and `git status`, and re-read this WO's register row, before your
176
+ > own review.** Across four ucm work orders the implementer produced an invalid review claim three
177
+ > times — twice by self-committing with a self-report, once by writing a **fabricated** verdict into
178
+ > `WORK_ORDERS.md`. The preamble now forbids editing that file; verify it was respected.
179
+
180
+ ### Mini-handover
181
+
182
+ Repo: `ui-core-micha` (`C:\Users\biglmi\Documents\webapps\ui-core-micha`), branch `main`.
183
+ WO: `work-orders/AUTH-4.md`. **Run after `SHELL-5`.** Follow `orchestrate-codex`.
@@ -0,0 +1,254 @@
1
+ # I18N-1 — One export, so a consumer cannot half-register the kit's strings
2
+
3
+ **Target repo:** `ui-core-micha` (branch `main`)
4
+ **Tier:** 3 — shared-core, and it adds a public export every consumer will depend on
5
+ **Target version:** `2.36.0` — minor. A new capability, fully backwards compatible.
6
+ **Prototype:** none. Nothing visual changes.
7
+
8
+ ---
9
+
10
+ ## A. Envelope
11
+
12
+ ### Goal
13
+
14
+ `ui-core-micha` exports its UI strings as **six separate bundles** that each consuming app must
15
+ spread into its own i18n resources by hand. Adopting a component without also adding its bundle
16
+ produces **a raw key rendered as visible UI text** — silently, with no type error and no runtime
17
+ error. Ship one aggregate export so that cannot happen, and so a *future* ucm feature is carried
18
+ automatically instead of requiring twelve apps to notice.
19
+
20
+ ### The evidence
21
+
22
+ **Two apps hit by the same mechanism, one of them still live.**
23
+
24
+ - **cockpit, 2026-08-11 (`UI-3`).** Adopted `UserMenu`; the matching `userMenuTranslations` was not
25
+ spread, and the header button rendered the literal string `UserMenu.TITLE`. **Both independent
26
+ static reviews were clean** — the defect is structurally invisible to `Read`/`Grep` review. It
27
+ was caught by the rendered click-through, and only by DOM inspection at that, because screenshot
28
+ capture was unavailable in that session. Fixed there.
29
+ - **fitness-monitor, live now.** Imports `ChartFrame` (`frontend/src/pages/BodyHistoryPage.jsx:7`,
30
+ used at `:295` and `:445`) but registers **only** `authTranslations`
31
+ (`frontend/i18n/index.js`), and carries no `ChartFrame.*` key of its own. So
32
+ `ChartFrame.jsx:74`'s `t('ChartFrame.LOADING')` and its sibling empty/error states render their
33
+ raw keys. Nobody noticed because those are transient edge states — which is exactly the class
34
+ `DESIGN.md` #7 says must be designed rather than left to chance.
35
+
36
+ **The estate survey, measured 2026-08-11.** Twelve apps register ucm bundles:
37
+
38
+ | App | Bundles registered | ucm components used beyond auth | Verdict |
39
+ |---|---|---|---|
40
+ | jg-ferien | all six + surveyRenderer | Bell, Settings, Wizard, UserMenu | correct |
41
+ | cockpit | auth, notifications, onboarding, userMenu | Bell, Settings, Wizard, UserMenu | correct (since `UI-3`) |
42
+ | hram | auth, charts, userMenu | ChartFrame, UserMenu | correct |
43
+ | **fitness-monitor** | **auth only** | **ChartFrame** | **defective** |
44
+ | spesix, survey_app, kerzenziehen, webshop-guenter, webapp-template, hpc-bridge, bigler-consult, survey_contact_app | auth only | none | correct today, exposed the moment they adopt anything |
45
+
46
+ Eight of the twelve are correct only because they have not yet adopted a second component. That is
47
+ not a safety property; it is a coincidence with an expiry date.
48
+
49
+ **The merge is safe.** All six bundles together hold **504 keys with zero collisions**, verified by
50
+ enumerating them; every key is namespace-prefixed (`Auth.*`, `ChartFrame.*`, `Messaging*.*`,
51
+ `NotificationBell.*`, `Onboarding.*`, `UserMenu.*`, plus the bare allauth error codes
52
+ `AUTH-2` deals with). A flat aggregate therefore changes no resolution today.
53
+
54
+ ### Scope
55
+
56
+ 1. **Export `uiCoreTranslations`** from the package root — the flat merge of all six bundles.
57
+ Consumers spread one thing instead of six.
58
+ 2. **Keep the six per-feature exports.** Removing them would break twelve apps for no gain; this
59
+ work order is additive. An app may spread both during migration — harmless, because the values
60
+ are identical and there are no collisions.
61
+ 3. **A ratchet test, and this is the part that makes the fix permanent.** Assert that the aggregate
62
+ contains every key of every bundle in `src/i18n/`, **enumerated from the directory rather than
63
+ from a hand-written list**. A new bundle added later that is not folded into the aggregate must
64
+ fail this test. Without that, the aggregate is correct today and rots the same way the six
65
+ exports did.
66
+ 4. **A collision test.** Assert the union has no duplicate key across bundles. It holds today at
67
+ 504 keys; the test is what stops a future bundle from silently shadowing an existing string.
68
+ 5. **Document the contract** where a consumer will actually meet it: one line in the README's i18n
69
+ section saying that spreading `uiCoreTranslations` is the supported way, and that the
70
+ per-feature exports remain for apps that want to be selective and accept the pairing burden.
71
+
72
+ ### Non-goals / do not touch
73
+
74
+ - **Self-registering components.** Feasible — ucm components use `useTranslation()` against the
75
+ consumer's own instance, so a component could call `addResourceBundle` itself — but it is a
76
+ separate question and a separate work order (`I18N-2`), deliberately: all twelve consumers flatten
77
+ everything into the **default namespace** with dotted keys, so the registration shape has to match
78
+ exactly, and registering at render time risks a first-paint flash of the raw key. That would
79
+ replace an invisible defect with a visible one. Framed as a bundle-size optimisation, not a
80
+ correctness fix, because this work order already closes the correctness hole.
81
+ - **Namespaces**, and the flat dotted-key shape generally. Twelve apps depend on it.
82
+ - **The bare allauth error codes** in `authTranslations` — `AUTH-2` owns those.
83
+ - **Any consumer repository.** See the rollout note below.
84
+ - Removing or renaming any existing export.
85
+
86
+ ### Risks
87
+
88
+ - **Bundle size for the eight auth-only apps.** `authTranslations` is 316 keys on its own; the other
89
+ five add 188, of which `messagingTranslations` is 116. An auth-only app that switches to the
90
+ aggregate ships roughly 30 KB of source strings it never renders. Judged acceptable against a
91
+ defect class that has already fired twice — but **measure the built delta and report it**, because
92
+ that number is the whole input to `I18N-2`, and a guess there would make that decision
93
+ unfalsifiable.
94
+ - **The ratchet test can be faked.** A test that enumerates the directory but compares against a
95
+ re-export (`export * from './i18n'`) would pass while proving nothing. It must compare *keys of
96
+ the aggregate value* against *keys read from each bundle module*.
97
+ - **Consumers spreading both during migration.** Safe today because of the zero-collision property
98
+ — which is precisely why item 4 is a test and not a comment.
99
+ - A parallel session is active in this repo (`THEME-*` work landed today up to `2.35.0`). Check
100
+ `git status` at start and sequence.
101
+
102
+ ### Rollout — and the constraint that shapes it
103
+
104
+ **A work order implements in one repository.** The twelve consumers cannot be migrated from here,
105
+ so this work order ships the export and the rollout lands as named rows in the consuming repos.
106
+
107
+ - **`fitness-monitor/FM-14` is mandatory and is the reason this is not merely hygiene** — it is the
108
+ app with a live defect. It is written and blocked on this work order's release.
109
+ **State the exposure plainly: fitness-monitor renders raw keys until `FM-14` lands.** The operator
110
+ chose to route the fix through this rollout rather than patch it separately, so that it cannot be
111
+ lost between two work orders; the cost of that choice is the delay, and it is recorded here rather
112
+ than left implicit.
113
+ - The other eleven apps are **correct today**. Migrating them to the aggregate is prevention, not
114
+ repair, and it can ride along the next time each is touched. Do not open eleven rows now.
115
+
116
+ ### Required tests to WRITE
117
+
118
+ 1. `uiCoreTranslations` contains every key of every module in `src/i18n/`, with the module list read
119
+ from the directory, not hand-written.
120
+ 2. The union has no duplicate key across bundles (504 today; assert the property, not the number).
121
+ 3. Every value keeps its `{ de, fr, en, sw }` shape through the merge — no bundle is flattened or
122
+ partially spread.
123
+ 4. The six per-feature exports still exist and are unchanged, so no consumer breaks.
124
+
125
+ Run ucm's own affected set, not the full suite: the i18n tests plus whatever imports from
126
+ `src/index.js`.
127
+
128
+ ### Parity guardrail
129
+
130
+ Purely additive. No existing export changes, no key changes, no rendered output changes in any app
131
+ that is correct today.
132
+
133
+ ---
134
+
135
+ ## B. Implementation map
136
+
137
+ *Filled by the Orchestrator on `git pull` — see `AGENTS.md` → "Work Order".*
138
+
139
+ ### Context package
140
+
141
+ **Bundle files** (all in `src/i18n/`, each `export const <name>Translations = { 'Key.NAME': { de, fr,
142
+ en, sw }, ... }`, flat object, no nesting):
143
+
144
+ - `src/i18n/authTranslations.ts` → `authTranslations` (316 keys, includes bare allauth codes like
145
+ `email_password_mismatch` alongside `Auth.*` keys — still a flat top-level key, no special casing
146
+ needed for the merge)
147
+ - `src/i18n/chartsTranslations.ts` → `chartsTranslations` (`ChartFrame.*`)
148
+ - `src/i18n/messagingTranslations.ts` → `messagingTranslations` (`Messaging*.*`, 116 keys)
149
+ - `src/i18n/notificationsTranslations.ts` → `notificationsTranslations` (`NotificationBell.*`)
150
+ - `src/i18n/onboardingTranslations.ts` → `onboardingTranslations` (`Onboarding.*`)
151
+ - `src/i18n/userMenuTranslations.ts` → `userMenuTranslations` (`UserMenu.*`)
152
+
153
+ **Root export site:** `src/index.js` — the six per-feature translation exports currently sit at two
154
+ places: `export { authTranslations } ... export { userMenuTranslations } ...` under the `// --- 7.
155
+ Translations ---` comment (~line 78-79), and `export { notificationsTranslations } ...
156
+ onboardingTranslations ... chartsTranslations ... messagingTranslations` under `// --- 10.
157
+ Translations ---` (~line 121-124). Add the new `uiCoreTranslations` export near those, importing all
158
+ six bundle modules and merging with a plain object spread (`{ ...authTranslations, ...chartsTranslations,
159
+ ... }`) — order doesn't matter, zero-collision is already verified. Do NOT remove or move the existing
160
+ six exports.
161
+
162
+ **Test file to add:** `tests/i18nAggregate.test.js` (new), following the directory-enumeration pattern
163
+ already used in `tests/themeCompleteness.test.js` (`fs.readdirSync` over a directory, don't hand-write
164
+ the file list) and the flat-object assertion style in `tests/authTranslations.test.js`. Enumerate
165
+ `src/i18n/*.ts`, `require`/dynamic-`import()` each module, find its exported `*Translations` object
166
+ (the module's only export), and:
167
+ 1. assert every key of every bundle is present in `uiCoreTranslations` with an identical value
168
+ (deep-equal, not just key presence — catches a bundle being spread partially);
169
+ 2. assert the union of all bundle keys has no duplicates (build a `Map<key, sourceModule>`, fail with
170
+ the two colliding module names if a key is seen twice);
171
+ 3. assert every value in `uiCoreTranslations` has the `{ de, fr, en, sw }` shape (all four locale keys
172
+ present, non-empty strings) — this must run against the aggregate, not against the individual
173
+ bundles, or it doesn't actually check the merge preserved shape.
174
+ Import `uiCoreTranslations` from `../src/index` (the public export), not from a private merge module —
175
+ the ratchet only means something if it tests what a consumer actually imports.
176
+
177
+ **Do not touch:** the six existing named exports, any bundle file's content/keys, `src/theme/**`
178
+ (`THEME-*` work is active in parallel — check `git status`/`git log` before starting and do not touch
179
+ theme files even incidentally).
180
+
181
+ **README:** add one line to the i18n section (`grep -n "i18n" README.md` to find it) stating
182
+ `uiCoreTranslations` is the supported aggregate import and the six per-feature exports remain for
183
+ apps that want to stay selective.
184
+
185
+ **Bundle-size measurement:** after implementing, report the built size delta for an auth-only app
186
+ switching from `authTranslations` alone to `uiCoreTranslations` — e.g. `du -h` or a byte count on the
187
+ relevant JSON/object literal, or diff `dist/index.js` chunk sizes before/after a `pnpm build` if that's
188
+ cheaper than hand-measuring. A rough KB figure is enough; this number feeds `I18N-2`, not this WO's
189
+ acceptance.
190
+
191
+ ### Target repo working directory (absolute)
192
+
193
+ `C:\Users\biglmi\Documents\webapps\ui-core-micha` — the repo root (no backend/frontend split here).
194
+
195
+ ### Preamble
196
+
197
+ > The text above is the COMPLETE spec — the committed WO file's content, not a plan to refine; there
198
+ > is no separate plan file. Read the nearest `AGENTS.md`, the relevant `.codex/skills/<role>/SKILL.md`,
199
+ > and the app `MEMORY.md` ONLY for conventions. Stay in scope; do not touch auth/permissions/deps/
200
+ > schema/CI unless the spec says so; do not update `MEMORY.md`. **Do NOT edit `WORK_ORDERS.md` — the
201
+ > register row and the review verdicts are the orchestrator's alone.** Do NOT `git add`/`commit`/
202
+ > `push` — leave every change uncommitted in the working tree for the orchestrator's independent
203
+ > review. WRITE the tests the `Required tests` section calls for AND **RUN the tests you just wrote**
204
+ > to confirm they execute and pass — that is the ONLY test run you do (NOT the app's affected/full
205
+ > suite, NOT any review). The orchestrator re-runs the authoritative set + does the independent review
206
+ > after you finish — those are the gate; your own run does not count as the gate.
207
+ >
208
+ > Narrate continuously: a `PLAN: <step1> | <step2> | …` line up front, then a single-line
209
+ > `PROGRESS: [<n>/<total>] <present-tense action>` before every relevant action (and `… done` on
210
+ > completion), spaced so no gap exceeds ~2 min, stdout unbuffered, plus exactly one final
211
+ > `RESULT: DONE|BLOCKED <reason>`.
212
+
213
+ ### Mini-handover
214
+
215
+ Repo: `ui-core-micha` (`C:\Users\biglmi\Documents\webapps\ui-core-micha`), branch `main`.
216
+ WO: `work-orders/I18N-1.md`. Target `2.36.0`. Downstream: `fitness-monitor/FM-14` is blocked on the
217
+ release and is the acceptance condition. Follow `orchestrate-codex`.
218
+
219
+ ---
220
+
221
+ ## C. Orchestrator only — NOT ADDRESSED TO THE IMPLEMENTER
222
+
223
+ > **If you are the implementer reading this work order as your own specification: STOP at this line.
224
+ > Everything below describes what the Orchestrator does AFTER you finish. You do none of it — no
225
+ > reviewers, no verification run, no register edit, no commit.** You ARE the invocation described
226
+ > below; do NOT shell out to `codex exec`.
227
+
228
+ ### Execution directive
229
+
230
+ Implement through `codex exec` in the background — invoked directly via Bash (never the
231
+ `debugger`/`*_coder` Agent wrappers) with BOTH flags `--skip-git-repo-check` and
232
+ `--dangerously-bypass-approvals-and-sandbox`. Fallback to direct Claude implementation only on Codex
233
+ quota/rate-limit/non-zero exit — the fallback flips authorship, so an independent reviewer becomes
234
+ mandatory.
235
+
236
+ ### Review routing
237
+
238
+ Independent `reviewer` **and** `ui_reviewer`, concurrent, same background batch. The `ui_reviewer`
239
+ pass is a formality here — the diff renders nothing — but Tier 3 asks for it on a frontend-touching
240
+ diff and a shared-core work order is the wrong place to argue an exemption.
241
+
242
+ ### Verification
243
+
244
+ No rendered check — this work order renders nothing. The verification that matters is `FM-14`'s:
245
+ `ChartFrame`'s loading and empty states in fitness-monitor must show real text. That is this work
246
+ order's real acceptance condition even though it is executed in another repo — record in the register
247
+ Notiz that `FM-14` is the pending downstream confirmation, so the export shipping is never mistaken
248
+ for the defect being fixed.
249
+
250
+ ### Register + commit
251
+
252
+ `WORK_ORDERS.md` row for `I18N-1`: `done` only once `reviewer` + `ui_reviewer` verdicts are named,
253
+ the affected-set tests pass, and the bundle-size delta is recorded in the Notiz. Commit to `main`
254
+ (this repo's trunk) on green.