@atomazing-org/design-system 2.0.1 → 2.0.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.
Files changed (23) hide show
  1. package/AGENTS.md +92 -0
  2. package/README.MD +45 -425
  3. package/dist/presets/index.d.ts +7 -1
  4. package/dist/presets/index.js +288 -306
  5. package/migrations/README.UPDATE.md +32 -20
  6. package/migrations/docs/migrations/design-system/README.md +48 -17
  7. package/migrations/docs/migrations/design-system/routes/adopt-existing/AEROCRM-EXAMPLE.md +197 -0
  8. package/migrations/docs/migrations/design-system/routes/adopt-existing/RUNBOOK.md +185 -25
  9. package/migrations/docs/migrations/design-system/routes/adopt-existing/migration.spec.json +68 -13
  10. package/migrations/docs/migrations/design-system/routes/greenfield/RUNBOOK.md +57 -25
  11. package/migrations/docs/migrations/design-system/routes/greenfield/migration.spec.json +28 -6
  12. package/migrations/docs/migrations/design-system/routes/mui4-to-latest/RUNBOOK.md +136 -77
  13. package/migrations/docs/migrations/design-system/routes/mui4-to-latest/migration.spec.json +89 -16
  14. package/migrations/docs/migrations/design-system/shared/WORKING-RULES.md +194 -0
  15. package/migrations/docs/migrations/design-system/shared/acceptance.md +76 -20
  16. package/migrations/docs/migrations/design-system/shared/common-regressions.md +218 -0
  17. package/migrations/docs/migrations/design-system/shared/gates.md +39 -3
  18. package/migrations/docs/migrations/design-system/shared/manual-qa-matrix.md +70 -0
  19. package/migrations/docs/migrations/design-system/shared/phase-exit-criteria.md +129 -0
  20. package/migrations/docs/migrations/design-system/shared/phases.md +377 -0
  21. package/migrations/skills/design-system-consumer-agent/SKILL.md +84 -0
  22. package/migrations/skills/design-system-migration-agent/SKILL.md +75 -9
  23. package/package.json +2 -1
@@ -0,0 +1,129 @@
1
+ # Phase Exit Criteria
2
+
3
+ Use this document with `shared/phases.md`.
4
+
5
+ Do not move to the next migration phase until the current phase satisfies its
6
+ exit criteria.
7
+
8
+ ## Phase 1: Route Selection And Scope
9
+
10
+ Exit when:
11
+
12
+ - the route is explicit
13
+ - the app root, theme source, shell, overlays, and protected routes are identified
14
+ - required validation commands are listed
15
+
16
+ Do not proceed if:
17
+
18
+ - route choice is still ambiguous
19
+ - the migration boundary is undefined
20
+
21
+ ## Phase 2: Baseline And Safety Net
22
+
23
+ Exit when:
24
+
25
+ - baseline lint, test, and build state is recorded
26
+ - pre-existing failures are separated from migration failures
27
+ - shell or route smoke coverage exists when broad shared rewrites are planned
28
+
29
+ Do not proceed if:
30
+
31
+ - you cannot distinguish old failures from new ones
32
+ - broad shell or overlay changes would be untested
33
+
34
+ ## Phase 3: Dependency Landing
35
+
36
+ Exit when:
37
+
38
+ - dependency changes install successfully
39
+ - route-incompatible packages are removed or isolated
40
+ - dependency conflicts are not blocking compile or startup work
41
+
42
+ Do not proceed if:
43
+
44
+ - install is still broken
45
+ - dependency state is half-migrated and unstable
46
+
47
+ ## Phase 4: Mechanical Migration Or Provider Landing
48
+
49
+ Exit when:
50
+
51
+ - the route-specific mechanical landing is complete
52
+ - the app compiles after the structural change
53
+ - the theme-root direction is clear and not duplicated
54
+
55
+ Do not proceed if:
56
+
57
+ - competing root providers still exist
58
+ - codemod or provider landing left the app uncompilable
59
+
60
+ ## Phase 5: Runtime Startup Cleanup
61
+
62
+ Exit when:
63
+
64
+ - the first route opens in a real browser
65
+ - startup console errors are fixed
66
+ - migration-caused startup warnings are fixed
67
+ - protected routes can render real content when they are in scope
68
+
69
+ Do not proceed if:
70
+
71
+ - the app is only build-green but not browser-runnable
72
+ - duplicate runtime warnings still remain unresolved
73
+
74
+ ## Phase 6: Shared Infrastructure Stabilization
75
+
76
+ Exit when:
77
+
78
+ - desktop and mobile shell layout behaves as intended
79
+ - page roots still fill the workspace
80
+ - shared padded containers do not overflow horizontally
81
+ - overlays keep stable semantics
82
+ - shared dashboards or compact controls do not show preset drift
83
+
84
+ Do not proceed if:
85
+
86
+ - the shell still has layout regressions
87
+ - shared dashboards, overlays, or route roots are visually or semantically broken
88
+
89
+ ## Phase 7: Token, Typography, And Primitive Cleanup
90
+
91
+ Exit when:
92
+
93
+ - visible text uses `Typography` with explicit variants
94
+ - zero-value primitive and surface wrappers are removed or collapsed
95
+ - token drift is reduced through `theme.*` usage and helpers
96
+ - localized copy remains readable if touched
97
+
98
+ Do not proceed if:
99
+
100
+ - raw text nodes, variantless typography, or visual-token drift are still spreading
101
+ - decorative consumer chrome is still replacing preset-owned surface behavior
102
+
103
+ ## Phase 8: Domain Migration Waves
104
+
105
+ Exit when:
106
+
107
+ - the current feature wave is validated independently
108
+ - route-specific smoke or lint checks are green again
109
+ - the app remains runnable before the next feature wave starts
110
+
111
+ Do not proceed if:
112
+
113
+ - the current wave reintroduced shared regressions
114
+ - multiple unfinished feature waves are open at once
115
+
116
+ ## Phase 9: Final Gates And Closeout
117
+
118
+ Exit when:
119
+
120
+ - shared gates and route-specific exit conditions are satisfied
121
+ - lint, build, tests, and smoke gates are green when available
122
+ - remaining warnings are either fixed or recorded as upstream-only by exact package
123
+ - manual QA follow-up is documented
124
+
125
+ Do not close the migration if:
126
+
127
+ - runtime startup still depends on manual excuses
128
+ - unresolved warnings are consumer-owned
129
+ - the app is not verified on a real route in a browser
@@ -0,0 +1,377 @@
1
+ # Shared Migration Phases
2
+
3
+ Use this document as the canonical step-by-step execution checklist for future
4
+ consumer app migrations to `@atomazing-org/design-system`.
5
+
6
+ Read this after route selection and before editing the target app.
7
+ Then combine it with:
8
+
9
+ - the selected route runbook
10
+ - the selected route `migration.spec.json`
11
+ - `shared/common-regressions.md`
12
+ - `shared/phase-exit-criteria.md`
13
+ - `shared/manual-qa-matrix.md`
14
+ - `shared/gates.md`
15
+ - `shared/acceptance.md`
16
+
17
+ ## How To Use This Playbook
18
+
19
+ 1. Detect the route first:
20
+ - `greenfield`
21
+ - `adopt-existing`
22
+ - `mui4-to-latest`
23
+ 2. Build the migration plan from the phases below.
24
+ 3. Keep the app runnable at the end of each phase.
25
+ 4. Run the relevant checks before moving to the next phase.
26
+ 5. Do not merge unrelated change classes into one unvalidated batch.
27
+
28
+ ## Phase Map
29
+
30
+ 1. Route selection and scope
31
+ 2. Baseline and safety net
32
+ 3. Dependency landing
33
+ 4. Mechanical migration or provider landing
34
+ 5. Runtime startup cleanup
35
+ 6. Shared infrastructure stabilization
36
+ 7. Token, typography, and primitive cleanup
37
+ 8. Domain migration waves
38
+ 9. Final gates and closeout
39
+
40
+ ## Phase 1: Route Selection And Scope
41
+
42
+ Goal:
43
+ Select the correct route and define the migration boundary before code changes.
44
+
45
+ Do:
46
+
47
+ - inspect `package.json`, imports, theme entrypoints, and current design-system usage
48
+ - choose the route:
49
+ - `mui4-to-latest` if `@material-ui/*` is present
50
+ - `greenfield` if the app is new and has no legacy theme or token layer
51
+ - `adopt-existing` otherwise
52
+ - identify:
53
+ - app root and provider wiring
54
+ - theme state source of truth
55
+ - shared shell and layout containers
56
+ - shared overlays
57
+ - protected routes
58
+ - token modules and hardcoded visual drift
59
+ - repo-local gates such as style, design, or typography contract scripts
60
+
61
+ Output:
62
+
63
+ - selected route and why
64
+ - initial migration scope
65
+ - list of required validation commands
66
+
67
+ Stop gate:
68
+
69
+ - the route is explicit
70
+ - the app areas at highest regression risk are named
71
+
72
+ Route notes:
73
+
74
+ - `greenfield`: scope is usually limited to provider wiring, app theme module, and startup safety
75
+ - `adopt-existing`: include provider, tokens, shared shell, shared overlays, and consumer primitives
76
+ - `mui4-to-latest`: also inventory legacy styling APIs, MUI v4 imports, and codemod impact zones
77
+
78
+ ## Phase 2: Baseline And Safety Net
79
+
80
+ Goal:
81
+ Capture the repo state before migration and make regressions visible early.
82
+
83
+ Do:
84
+
85
+ - run the repo's normal commands:
86
+ - `npm run lint` when present
87
+ - `npm test` when present
88
+ - `npm run build`
89
+ - run repo-local contract gates when present:
90
+ - `npm run lint:style-contract`
91
+ - `npm run lint:design-contract`
92
+ - `npm run lint:typography-contract`
93
+ - record pre-existing red checks separately from migration defects
94
+ - add or tighten smoke coverage before broad rewrites when the app has:
95
+ - a shared shell
96
+ - protected routes
97
+ - shared overlays
98
+ - dashboards, analytics, or dense workflow screens
99
+
100
+ Output:
101
+
102
+ - baseline check matrix
103
+ - known pre-existing failures
104
+ - smoke coverage plan
105
+
106
+ Stop gate:
107
+
108
+ - you can tell whether a later failure is new or pre-existing
109
+ - shell-critical or overlay-critical changes are covered by at least one route check
110
+
111
+ Route notes:
112
+
113
+ - `greenfield`: a simple startup check may be enough
114
+ - `adopt-existing`: shell and at least one real content route should be covered
115
+ - `mui4-to-latest`: add smoke coverage before codemod fallout reaches shared UI
116
+
117
+ ## Phase 3: Dependency Landing
118
+
119
+ Goal:
120
+ Land the dependency set needed for the target route without mixing in broad UI cleanup.
121
+
122
+ Do:
123
+
124
+ - update `package.json` directly
125
+ - add or align:
126
+ - `@atomazing-org/design-system`
127
+ - `@mui/material`
128
+ - `@mui/icons-material` when needed
129
+ - `@emotion/react`
130
+ - `@emotion/styled`
131
+ - remove route-incompatible packages such as `@material-ui/*`
132
+ - install dependencies with the repo's package manager
133
+ - if install scripts fail because of local tooling, retry with the package manager's `--ignore-scripts` equivalent only as recovery
134
+
135
+ Output:
136
+
137
+ - dependency set aligned with the chosen route
138
+ - install completed
139
+
140
+ Stop gate:
141
+
142
+ - the repo installs successfully
143
+ - dependency conflicts do not block the next phase
144
+
145
+ Route notes:
146
+
147
+ - `greenfield`: this is usually the main dependency phase
148
+ - `adopt-existing`: keep adjacent runtime alignment small and controlled
149
+ - `mui4-to-latest`: remove legacy Material-UI packages before broad theme unification
150
+
151
+ ## Phase 4: Mechanical Migration Or Provider Landing
152
+
153
+ Goal:
154
+ Reach the route-specific structural landing point before visual cleanup.
155
+
156
+ Do:
157
+
158
+ - `greenfield`:
159
+ - connect `ThemeProviderWrapper` at the app root
160
+ - create one explicit app theme module for preset choice when needed
161
+ - add theme controls only through `useThemeSettings()`
162
+ - `adopt-existing`:
163
+ - connect or normalize `ThemeProviderWrapper`
164
+ - remove competing root theme providers
165
+ - replace outdated design-system imports with public APIs or local app abstractions
166
+ - move preset selection into one app-owned theme module
167
+ - `mui4-to-latest`:
168
+ - run the official MUI codemod
169
+ - resolve compile breaks from the codemod
170
+ - replace JSS-only APIs and legacy theme APIs
171
+ - get the app stable on modern MUI plus Emotion before provider unification
172
+
173
+ Output:
174
+
175
+ - route-specific mechanical landing completed
176
+
177
+ Stop gate:
178
+
179
+ - the app compiles after the structural change
180
+ - there is one clear theme-root direction
181
+
182
+ ## Phase 5: Runtime Startup Cleanup
183
+
184
+ Goal:
185
+ Prove the app actually boots in a browser and remove startup defects before deeper cleanup.
186
+
187
+ Do:
188
+
189
+ - start the app with the normal dev or start command
190
+ - open the first route in a real browser
191
+ - fix:
192
+ - browser console startup errors
193
+ - migration-caused startup warnings
194
+ - duplicate runtime singleton loading for `react`, `react-dom`, `@emotion/react`, `@emotion/styled`
195
+ - remove module-scope code that depends on:
196
+ - `window`
197
+ - `document`
198
+ - `navigator`
199
+ - storage
200
+ - i18n locale activation
201
+ - move browser listeners into effects with cleanup
202
+ - if smoke mode bypasses auth, seed a deterministic role-bearing user for protected routes
203
+
204
+ Output:
205
+
206
+ - startup-clean browser boot
207
+
208
+ Stop gate:
209
+
210
+ - first route loads successfully
211
+ - migration-caused startup errors are gone
212
+
213
+ ## Phase 6: Shared Infrastructure Stabilization
214
+
215
+ Goal:
216
+ Stabilize the app shell and shared UI contracts before domain-by-domain feature work.
217
+
218
+ Do:
219
+
220
+ - preserve shell layout behavior:
221
+ - display mode
222
+ - grid or flex template
223
+ - `min-width`
224
+ - `min-height`
225
+ - `overflow`
226
+ - scroll behavior
227
+ - keep route roots and page containers filling the intended workspace
228
+ - if shared padded containers also fill width, normalize with `box-sizing: border-box` or remove forced width
229
+ - restore overlay semantics:
230
+ - stable `aria-labelledby`
231
+ - stable `aria-describedby`
232
+ - labeled close actions
233
+ - `combobox` and `listbox` semantics for searchable overlays
234
+ - audit shared charts, legends, chips, metric badges, and segmented filters for preset drift
235
+ - remove placeholder regressions and low-fidelity pass-through wrappers
236
+ - if analytics dashboards or metric cards are touched:
237
+ - keep labels and values on different hierarchy levels
238
+ - keep one explicit grid gap contract
239
+ - remove accidental full-width spans that break desktop rows
240
+
241
+ Output:
242
+
243
+ - stable shared shell
244
+ - stable shared overlays
245
+ - stable shared dashboard or compact-control contracts
246
+
247
+ Stop gate:
248
+
249
+ - desktop and mobile shell behavior matches the intended layout
250
+ - shared UI is usable and semantically wired
251
+
252
+ ## Phase 7: Token, Typography, And Primitive Cleanup
253
+
254
+ Goal:
255
+ Move the consumer toward the design-system contract instead of preserving legacy local drift.
256
+
257
+ Do:
258
+
259
+ - replace legacy tokens with `theme.palette`, `theme.typography`, and helpers such as `alpha()`
260
+ - normalize visible text:
261
+ - `Typography`
262
+ - explicit semantic `variant`
263
+ - `Typography component="span"` for visible text inside interactive controls
264
+ - remove zero-value wrappers:
265
+ - primitive wrappers
266
+ - surface wrappers
267
+ - simplify decorative consumer chrome that fights the preset:
268
+ - gradients
269
+ - direct shadow styling
270
+ - palette alpha suffix hacks
271
+ - if the repo needs guardrails, add or tighten:
272
+ - design-contract lint
273
+ - typography-contract lint
274
+ - if localized copy was touched, scan for mojibake or replacement characters and verify a real localized route
275
+
276
+ Output:
277
+
278
+ - token and text layer aligned with the design-system contract
279
+
280
+ Stop gate:
281
+
282
+ - typography and design drift are reduced, not just moved around
283
+ - the repo has enough local gates to keep the fix from regressing
284
+
285
+ ## Phase 8: Domain Migration Waves
286
+
287
+ Goal:
288
+ Migrate feature areas in controlled batches after shared infrastructure is stable.
289
+
290
+ Do:
291
+
292
+ - move feature areas one wave at a time
293
+ - keep each wave small enough to validate independently
294
+ - after each wave, re-run the relevant checks:
295
+ - smoke
296
+ - lint
297
+ - route checks
298
+ - build when the wave changes bundling or shared runtime behavior
299
+ - stop if a wave reintroduces shell, overlay, typography, or token regressions
300
+
301
+ Output:
302
+
303
+ - migrated domains with isolated validation history
304
+
305
+ Stop gate:
306
+
307
+ - each wave ends in a runnable, validated state
308
+
309
+ ## Phase 9: Final Gates And Closeout
310
+
311
+ Goal:
312
+ Close the migration only after runtime, route, and contract checks are all green.
313
+
314
+ Do:
315
+
316
+ - re-run:
317
+ - `npm run lint`
318
+ - `npm test`
319
+ - `npm run build`
320
+ - `npm run test:smoke` when present
321
+ - repo-local contract gates when present
322
+ - verify route-specific exit conditions:
323
+ - no `@material-ui/*` left for `mui4-to-latest`
324
+ - provider wiring and preset ownership are correct
325
+ - no migration-caused startup warnings remain
326
+ - record any remaining upstream-only warnings precisely by package and file
327
+ - write the migration summary:
328
+ - route used
329
+ - commands run
330
+ - startup issues fixed
331
+ - remaining manual QA
332
+ - rollback guidance if partial
333
+
334
+ Output:
335
+
336
+ - migration report suitable for future reuse
337
+
338
+ Stop gate:
339
+
340
+ - shared acceptance is satisfied
341
+ - the app is browser-runnable, not only build-green
342
+
343
+ ## Recommended Phase Splits By Route
344
+
345
+ `greenfield`:
346
+
347
+ 1. Route selection and baseline
348
+ 2. Dependency landing
349
+ 3. Provider landing
350
+ 4. Runtime startup cleanup
351
+ 5. Final gates
352
+
353
+ `adopt-existing`:
354
+
355
+ 1. Route selection and baseline
356
+ 2. Safety net
357
+ 3. Dependency landing
358
+ 4. Provider and theme unification
359
+ 5. Runtime cleanup
360
+ 6. Shared infrastructure stabilization
361
+ 7. Token and typography cleanup
362
+ 8. Domain waves
363
+ 9. Final gates
364
+
365
+ `mui4-to-latest`:
366
+
367
+ 1. Route selection and baseline
368
+ 2. Safety net
369
+ 3. Dependency landing
370
+ 4. Mechanical MUI migration
371
+ 5. Legacy styling cleanup
372
+ 6. Provider and theme unification
373
+ 7. Runtime cleanup
374
+ 8. Shared infrastructure stabilization
375
+ 9. Token and typography cleanup
376
+ 10. Domain waves
377
+ 11. Final gates
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: design-system-consumer-agent
3
+ description: Rules for integrating and maintaining @atomazing-org/design-system in application repos. Use when an AI agent needs to add the library, wire it into an app, or decide whether the request should become a route-based migration.
4
+ ---
5
+
6
+ # Design System Consumer Agent
7
+
8
+ Use this skill whenever the task is about adopting, integrating, or maintaining
9
+ `@atomazing-org/design-system` in an application repository.
10
+
11
+ ## Source Of Truth
12
+
13
+ 1. Read `../../docs/migrations/design-system/shared/WORKING-RULES.md`.
14
+ 2. Use `../../docs/migrations/design-system/README.md` for route selection context.
15
+ 3. If migration is needed, switch to `../design-system-migration-agent/SKILL.md`.
16
+
17
+ ## Default Workflow
18
+
19
+ 1. Inspect the target app:
20
+ - current dependencies
21
+ - current theme provider setup
22
+ - theme or token modules
23
+ - SSR or Next.js usage
24
+ 2. Apply the working rules from `WORKING-RULES.md`.
25
+ 3. Decide whether the task is:
26
+ - a normal integration task
27
+ - or a route-based migration
28
+
29
+ ## Escalate To Migration
30
+
31
+ Escalate to the migration workflow if any of these are true:
32
+
33
+ - `@material-ui/*` exists
34
+ - the app already has a substantial theme/token system
35
+ - the request would leave dual theme providers or dual sources of truth
36
+ - the user asks to standardize or replace the design foundation
37
+ - changes affect a large set of colors, typography, or component overrides
38
+
39
+ When escalation is needed:
40
+
41
+ 1. State that route-based migration is recommended.
42
+ 2. Select the route:
43
+ - `mui4-to-latest`
44
+ - `greenfield`
45
+ - `adopt-existing`
46
+ 3. Load `../design-system-migration-agent/SKILL.md`.
47
+ 4. For large repos, execute the selected route phase by phase instead of batching broad edits into one pass.
48
+
49
+ ## Normal Integration Expectations
50
+
51
+ When migration is not needed:
52
+
53
+ - use public imports only
54
+ - prefer `ThemeProviderWrapper`
55
+ - prefer `useThemeSettings()`
56
+ - use `defaultThemes`, `landingPageThemes`, or `allBuiltInThemes` intentionally
57
+ - keep preset choice in one explicit app-owned theme module instead of generic constants passthrough files
58
+ - let the active preset own the visual treatment of `Paper`, `Card`, `Dialog`, `Drawer`, and similar surfaces across the app
59
+ - do not hand-style consumer surfaces with app-local glow, bloom, blur, decorative borders, gradient washes, or pseudo-element chrome; those belong in presets/theme overrides
60
+ - keep app styling focused on layout, spacing, sizing, and content hierarchy
61
+ - if the app shell exposes a dedicated workspace or content pane, keep route roots and page containers stretched to fill it instead of shrinking to intrinsic content width
62
+ - for landing and marketing surfaces, prefer MUI-native preview composition over custom decorative UI logic
63
+ - do not hand-style landing surfaces with app-local glow, bloom, blur, decorative borders, gradient washes, or pseudo-element chrome; those belong in presets/theme overrides
64
+ - do not build nested rectangular surface hierarchies such as `card inside card inside card`
65
+ - keep one primary surface per logical block and use `Stack`, `List`, `Divider`, `Typography`, `Chip`, and `Avatar` before introducing another bordered layer
66
+ - keep landing preview blocks spacious and reduce content before compressing spacing
67
+ - remove zero-value wrappers that only rename MUI primitives or surfaces
68
+ - do not leave large inline `sx` objects in JSX; if a component needs substantial styling, extract a local `styled(...)` component in the same file and keep those styled declarations near the bottom
69
+ - if multiple role pages share the same shell, extract a shared workflow layout before changing visuals independently
70
+ - if a local barrel creates chunk-cycle warnings, prefer direct local imports for the affected workflow internals
71
+ - fix consumer-owned build warnings such as stale PWA asset globs or oversized local chunks instead of silencing them
72
+ - if a remaining warning belongs to an upstream toolchain package, record it precisely as external debt
73
+ - keep visible text on `Typography` with explicit semantic variants instead of raw JSX text nodes
74
+ - when buttons, chips, alerts, menu items, or toggle buttons show text, wrap that label in `Typography component="span"`
75
+ - keep compact legends, chips, segmented filters, and metric badges visually aligned with the active preset instead of restyling them into ad hoc chip-like chrome
76
+ - when shared dialogs, drawers, or bottom sheets are touched, keep stable `aria-labelledby` and `aria-describedby` wiring plus labeled close actions
77
+ - when an overlay owns filtered keyboard navigation, use `combobox` and `listbox` semantics instead of generic wrapper markup
78
+ - keep browser event listeners such as `beforeinstallprompt` or `matchMedia` subscriptions inside effects with cleanup
79
+ - remove zero-value surface wrappers that only forward to MUI `Card` or `Paper`
80
+ - use `alpha()` or equivalent theme helpers instead of palette-string alpha suffix hacks
81
+ - treat decorative gradients and shadow styling as migration defects unless the component is an explicit preview exception documented in the repo-local design-contract gate
82
+ - keep browser-only APIs out of module scope
83
+ - after dependency or provider wiring changes, start the app and verify the first route boots without browser console startup errors
84
+ - if bulk rewrites or localized copy changes were part of the work, keep files in UTF-8, scan for mojibake or replacement characters, and verify readable text on a real route