The plan's last phase becomes a record, and the document gains a closing status:
every count the audit was written to move, from the state in "Where this app
stands" to what `m3-audit.sh` reports today, and a gathered list of what a person
still has to look at — the eight screens with competing filled buttons, the two
list-detail families the pane work did not reach, the container transform, desktop
keyboard traversal, and the avatar picker's selected state.
**The audit caught the previous commit.** `ThemeGallery` added eight string
literals in composables, taking the count 39 -> 47, which is exactly the drift the
budget exists to notice. They are sample text — the words are chosen to be words,
so that colour pairings can be looked at — and putting them in the catalogue would
add eight entries no screen shows and a translator would have to be told to
ignore.
So the audit grows a third exemption marker beside `m3-color-exempt` and
`m3-spacing-exempt`: `m3-string-exempt`, per file rather than per line, because
the exemption is a property of what the file is for and eight markers down one
gallery would say less than one at the top of it. Back to 39, and the report now
says how many files are exempt so the mechanism cannot be used quietly.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Phase 8, item four. This repository had no `CLAUDE.md` at all, so eight phases of
decisions lived only in `docs/material-design-conformance.md` -- a 980-line
account that is the right place to explain *why* and the wrong place to look
while writing a screen.
Seven rules, each with the shape to copy, the shape not to, and the budget the
audit holds it to: spacing from the scale, colour from a role, text from the
catalogue in sentence case, 48dp targets with decided icon descriptions, four
screen states with a transition between them, layout that reads the window rather
than the composable, motion from the scheme.
Where a rule has a trap that has already caught somebody, the trap is named rather
than the rule restated: `.copy(alpha = …)` on a content role is how nine contrast
failures got in; Compose Resources unescapes `\n` but not `\'`; `AnimatedContent`
is a layout node and takes a `when`'s branches out of `ColumnScope`;
`MotionSchemeKeyTokens` is internal and cannot be reached from an app;
`rememberNotifier` and `stringResource` are composable and an `onClick` is not.
Every API named in it was checked to exist, and the notifier example was corrected
after the first draft got its signature wrong -- it takes the caller's scope, so
that a message shown as a screen navigates away is not cancelled with it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>