| name | diagnostics-ux-review |
| description | Audit the library's friendly compile-time diagnostics end-to-end — trigger each one, verify the message fires before raw template noise, and prove its recommended remedy actually compiles. Use after changing concepts/asserts/operators, or when a user reports a confusing compile error. |
Purpose
In a template library, compile errors are the user interface: most users
meet static_assert("MIP++: ...") messages long before they read docs.
This skill audits those diagnostics like UI — every one deliberately
triggered, its noise measured, its remedy verified — instead of trusting
that a message written once still fires, still fires first, and still
gives advice that compiles.
When to use
- After adding/renaming/reordering concepts,
assert_* helpers, or the
operators that call them.
- After changing what an error message's remedy refers to (e.g.
materialize, std::move, "build over a named range").
- When a user reports an incomprehensible template error — the audit tells
you whether a diagnostic is missing or merely buried.
- Periodically, since diagnostics have no tests that fail when they rot.
The diagnostic system (invariants)
- Friendly diagnostics are
consteval void assert_*() helpers in detail,
with messages prefixed "MIP++: ". Inventory them with:
grep -rn "consteval void assert_" include/ and
grep -rn "MIP++:" include/.
- Operators call the assert as the first statements of the body, before
the
return that instantiates the view class — statements instantiate in
order, so the friendly message prints before any constraint failure from
the class-level requires. That ordering is load-bearing: never move the
assert after the return expression, and never delete the class requires
in its favor (see cpp-template-library-review).
- The docs quote these messages in a troubleshooting table
(
docs/reference/expression-layer.md); message edits propagate there
(see api-doc-sync).
- The build adds
-fconcepts-diagnostics-depth=30 for readable concept
failures; scratch audits should compile with the same flag.
Workflow: full audit
- Inventory: list every assert helper, its message, the operators that
call it, and the remedy each message promises. A helper no operator
calls, or an operator consuming operands without the matching assert, is
finding #1.
- Write one trigger TU per diagnostic in the scratchpad — the minimal
user code that commits the mistake the message describes (single-use
lvalue reused,
xsum result squared, mixed scalar types...). Compile
with the quick syntax-check command.
- For each trigger, verify four properties:
- Verify every remedy compiles. Take the failing TU and apply each fix
the message proposes, literally as worded. Each variant must compile and
behave. A remedy that itself fails is a real bug class (a
materialize
escape hatch that produces a type frozen against the next step was found
exactly this way).
- Cover the mirrors. Trigger via lvalue and rvalue operands, both
operand orders, and through every operator sharing the assert (
+, -,
xsum, +=...). An assert present in operator+ but missing in
operator+= was a real gap.
- Verify the silent path: the nearest correct usages must compile
with zero warnings under
-Wall — a diagnostic that fires on valid code
is worse than noise.
- Report per diagnostic: fires? position in stderr? remedy valid? gaps.
Fix message text, assert placement, and missing call sites; sync docs.
Message style rules (for new or edited diagnostics)
Quality checklist
Common mistakes to avoid
- Testing that the code fails to compile without checking which error
the user actually sees first.
- Editing a message's advice without recompiling the advice.
- Renaming an API and leaving its old name inside message strings and the
docs table — neither is compiled.
- Adding a new consuming operation (or accessor) without wiring the assert,
leaving raw
deleted function errors as its diagnostic.
- Auditing only rvalue happy-path triggers; the lvalue/const mirrors take
different overload paths and can bypass the assert.