Implementation Audit
Instructions
Audit the implementation of $ARGUMENTS (a use case ID like UC-XXX) against its specification,
design, and quality standards. This is an independent quality review intended to catch issues
that unit and E2E tests do not surface.
Run the lenses below sequentially. Collect all findings before producing the final report.
Inputs
| Input |
Location |
| Use case specification |
docs/use_cases/$ARGUMENTS.md |
| Frontend design |
docs/designs/$ARGUMENTS-design.html |
| Entity model |
docs/entity_model.md |
| Definition of Done |
${CLAUDE_PLUGIN_ROOT}/shared/readiness/DEFINITION_OF_DONE.md |
| i18n package |
internal/i18n/i18n.go (supported locales, T, TN, Locale) |
| Message catalogs |
internal/i18n/locales/*.toml (one per locale) |
DO NOT
- Modify any implementation files — this is a read-only audit
- Scan generated code (
*_templ.go, internal/db/) — audit the .templ, .sql, and hand-written .go sources
- Flag issues already verified by passing E2E tests unless the test assertion itself is wrong
- Suggest architectural changes beyond what the spec requires
i18n Detection
Before running Lenses 1–3, check whether the project uses i18n:
- Look for
<!-- NEXA_I18N_CONFIGURED --> in the project's CLAUDE.md, or
- Check for an
internal/i18n/ directory, or github.com/nicksnyder/go-i18n in go.mod
If none found, mark Lenses 1–3 as N/A and skip them.
Lenses
Lens 0: Definition of Done (file analysis — no browser)
Read the Definition of Done checklist. For every item, independently verify whether the
implementation satisfies it by reading the code, spec, and entity model.
For each item, report:
- The item name
- PASS or FAIL with evidence (file paths, line numbers, observations)
- If FAIL: what is missing or incorrect
Key verification points:
- Task Completeness — Cross-reference every MSS step, alternative flow, business rule,
precondition, and postcondition from the spec against the handlers, services, and views
- Acceptance Criteria — Read the acceptance criteria from the spec file (
docs/use_cases/UC-XXX.md) for $ARGUMENTS; verify each criterion is satisfiable
- Code Quality — Run
go tool templ generate && sqlc generate && go build ./... && go vet ./...;
verify form Validate() runs on every mutation at the system boundary; verify error states
surface meaningful feedback
- Test Coverage — Verify unit tests exist next to the code, as
/implement prescribes:
form_test.go (validation rules), service_test.go (business rules, returned errors),
handler_test.go (status codes, 422, fragment vs full page, redirects), views_test.go
(empty-state and error text). Every MSS step, alternative flow, and business rule is asserted
- Privacy — Search for hardcoded secrets; verify
.gitignore covers .env and .env.*
- i18n — Only check if i18n is detected (see i18n Detection above); otherwise mark N/A
- Configuration Management — Verify environment profiles (
.env, .env.dev) and
internal/config/config.go exist
Lens 1: i18n Completeness (file analysis — no browser) — skip if no i18n
Identify every user-facing string introduced or modified for $ARGUMENTS:
- Verify each goes through
i18n.T(ctx, ...) or i18n.TN(ctx, ...), not a hardcoded literal.
Search the feature's .templ and .go files for:
- Text nodes in
.templ files not wrapped in { i18n.T(ctx, "...") }
- Attribute literals (
placeholder, title, aria-label, alt, value on buttons) that
do not go through i18n.T
- Literal messages returned by
Validate() — with i18n configured, Validate() returns
field → message ID and the view translates it
http.Error bodies and error fragments rendered to the user
- Verify every message ID used exists in all catalogs under
internal/i18n/locales/
- Flag IDs missing from non-default locales
- Flag values with a
[TRANSLATE] prefix (placeholder not yet translated)
Lens 2: i18n Correctness (file analysis — no browser) — skip if no i18n
For every message ID used by $ARGUMENTS, compare values across all catalogs:
- Verify template placeholders (
{{.Min}}, {{.Name}}) match across locales — same names,
same count — and match the data passed at the T / TN call site
- Verify plural messages (used with
TN) define every CLDR form the locale needs — one and
other everywhere, plus few / many where the locale requires them (e.g. ro, pl, ru)
- Flag translations that appear machine-translated or identical to the default locale
- Flag translations where meaning clearly diverges from the default
Lens 3: Error Message i18n (file analysis — no browser) — skip if no i18n
Search all implementation files for $ARGUMENTS and identify every error-handling path:
- Handler error mapping — not found →
404, forbidden → 403, anything else → 500
422 form re-renders with field errors and the non-field error summary
- htmx error fragments
- The
Recover middleware's 500 response
- The
401 + HX-Redirect and 403 responses from internal/auth
For each, verify the message shown to the user uses a message ID, not a hardcoded string.
Hardcoded strings like "Something went wrong", "Invalid input", or "Please try again" are
findings. Log messages written with slog are not user-facing — do not flag them.
Lens 4: Accessibility (Playwright MCP — browser required)
Start the app if not running: set -a; . ./.env; set +a; go run ./cmd/dev. For each screen in
the frontend design:
- Navigate using
browser_navigate
- Run axe-core. The app's CSP (
script-src 'self', set by /setup-web-middleware) blocks
importing axe-core from a CDN inside browser_evaluate. Inject it from the Node side with
browser_run_code_unsafe instead:async (page) => {
const src = await (await fetch('https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.9.1/axe.min.js')).text();
await page.evaluate(src);
return await page.evaluate(() => axe.run());
}
- Collect all violations with impact level (critical, serious, moderate, minor)
- Additionally check:
- Every
<img> has a non-empty alt (or alt="" with role="presentation" for decorative)
- Every form input has
<label for> or aria-label
- Focus order is logical (tab through with
browser_press_key)
- Focus lands somewhere sensible after an htmx swap (e.g. the first field error)
- Color contrast meets WCAG AA (axe-core covers this, but flag if axe is unavailable)
Lens 5: Screen Fidelity vs Design (Playwright MCP — browser required)
For each screen in the frontend design:
- Open the design HTML in the browser (
browser_navigate to the file path)
- Take a snapshot (
browser_snapshot)
- Navigate to the implemented screen in the running app
- Take a snapshot
- Compare and flag:
- Missing components (in design, absent in implementation)
- Layout deviations (arrangement, alignment, spacing)
- Typography mismatches (headings, font sizes, font weights)
- Color mismatches (background, text, border)
- Missing states (design specifies an empty state that implementation doesn't handle)
Lens 6: Loading, Error, and Empty States (file analysis + Playwright MCP)
For each screen that performs async operations (htmx requests, form submissions):
- Verify every triggering element has
hx-indicator, and internal/web/static/app.css defines
the .htmx-indicator rules (htmx's inline indicator styles are disabled by the CSP setup)
- Verify the layout's
htmx-config responseHandling swaps 422, so re-rendered forms with
validation errors actually appear
- Submit invalid input in the browser: field errors appear next to their inputs, and non-field
errors appear in a summary
- Verify data screens render an explicit empty-state component (no rows → meaningful message,
not a blank page)
- Navigate to a missing resource (e.g.
/items/does-not-exist): the app responds 404 with a
page, not a 500 or a blank body
- Verify a
5xx on an htmx request shows the user something (an error fragment, a swapped
error target, or an htmx:responseError handler in a static .js file) rather than silently
doing nothing
Lens 7: E2E Traceability (file analysis — no browser)
Skip if e2e/trace_test.go does not exist (project has not adopted the traceability helper
yet).
Otherwise, for every file e2e/*_test.go except trace_test.go and setup_test.go:
Build the ignore set. If e2e/.tracedignore exists, parse it as a gitignore-style list
(one path per line, # comments, blank lines skipped) and exclude matching files from the
rest of the lens.
Check top-level test names. Every top-level func TestXxx(t *testing.T) (excluding
TestMain) is named TestUC<NNN> or TestBUG<NNN>. Any other name is a violation.
Check TestUC<NNN> subtests. Every t.Run("<scenario>/<journey>", ...) inside
TestUC<NNN> starts with useCase(t, "UC-NNN", "<scenario>", refs...), where:
- the
UC-NNN argument matches the enclosing test name (TestUC007 → "UC-007")
- the scenario argument is
MSS, AF-N, or EX-N and matches the t.Run name prefix
A subtest whose first statement is not useCase(...), or whose arguments disagree with the
test or subtest name, is a violation.
Check TestBUG<NNN> tests. The first statement is bug(t, "BUG-NNN") with the ID
matching the test name.
Check references. For every useCase(t, "UC-NNN", scenario, "CR-NNN", "BUG-NNN", ...)
and bug(t, "BUG-NNN") literal: confirm the referenced doc exists under docs/use_cases/,
docs/change_requests/, or docs/bugs/. The helper enforces this at runtime; the audit
catches it before a test run.
Check the status of each referenced BUG-NNN. If the bug file shows a status of
RESOLVED (or equivalent), this is correct. If the status is still OPEN, the test is
guarding against a regression of an unfixed bug — flag as Minor and ask whether the bug
should be marked resolved.
For each violation, report file path, line number, and the specific rule broken. Severity:
Major during rollout (advisory). Once one full cluster has been delivered cleanly under the
helper, escalate to Critical and have the audit fail the merge gate.
Output Format
Produce one section per lens. For each finding:
- Severity: Critical / Major / Minor
- Location: file path and line number (or screen name for browser lenses)
- Finding: what is wrong
- Fix: how to fix it
Severity guide:
- Critical — DoD item FAIL, user sees broken/untranslated text, app crashes or panics, WCAG A violation
- Major — Missing translation in non-default locale, WCAG AA violation, missing error state,
missing unit tests for a business rule or alternative flow
- Minor — Placeholder not yet translated, minor style deviation, missing decorative alt text
Verification
The audit is complete when:
- Every lens has a section marked with findings, no findings, or N/A with the reason
- Every finding has severity, location, finding, and fix
- The report ends with a summary:
- Total findings by severity
- Verdict: PASS (no findings) | PASS WITH OBSERVATIONS (0 Critical, Minor only) | FAIL (any Critical or Major)
1---2name: audit3description: Runs a comprehensive implementation audit for a delivered use case in a Go web application. Checks Definition of Done, i18n completeness and correctness, error message i18n, accessibility, visual fidelity against the design, htmx loading/error/empty states, and E2E traceability. Run after /deliver-use-case or before /merge-use-case for a quality deep-dive. Use when the user asks to "audit UC-XXX", "run a quality audit", "check accessibility", "check translations", or mentions a deep quality review. Results are advisory — Critical and Major findings should be fixed; Minor findings are observations.4---56# Implementation Audit78## Instructions910Audit the implementation of $ARGUMENTS (a use case ID like `UC-XXX`) against its specification,11design, and quality standards. This is an independent quality review intended to catch issues12that unit and E2E tests do not surface.1314Run the lenses below sequentially. Collect all findings before producing the final report.1516## Inputs1718| Input | Location |19|-------|----------|20| Use case specification | `docs/use_cases/$ARGUMENTS.md` |21| Frontend design | `docs/designs/$ARGUMENTS-design.html` |22| Entity model | `docs/entity_model.md` |23| Definition of Done | `${CLAUDE_PLUGIN_ROOT}/shared/readiness/DEFINITION_OF_DONE.md` |24| i18n package | `internal/i18n/i18n.go` (supported locales, `T`, `TN`, `Locale`) |25| Message catalogs | `internal/i18n/locales/*.toml` (one per locale) |2627## DO NOT2829- Modify any implementation files — this is a read-only audit30- Scan generated code (`*_templ.go`, `internal/db/`) — audit the `.templ`, `.sql`, and hand-written `.go` sources31- Flag issues already verified by passing E2E tests unless the test assertion itself is wrong32- Suggest architectural changes beyond what the spec requires3334## i18n Detection3536Before running Lenses 1–3, check whether the project uses i18n:37- Look for `<!-- NEXA_I18N_CONFIGURED -->` in the project's `CLAUDE.md`, or38- Check for an `internal/i18n/` directory, or `github.com/nicksnyder/go-i18n` in `go.mod`3940If none found, mark Lenses 1–3 as **N/A** and skip them.4142## Lenses4344---4546### Lens 0: Definition of Done (file analysis — no browser)4748Read the Definition of Done checklist. For every item, independently verify whether the49implementation satisfies it by reading the code, spec, and entity model.5051For each item, report:521. The item name532. **PASS** or **FAIL** with evidence (file paths, line numbers, observations)543. If FAIL: what is missing or incorrect5556Key verification points:57- **Task Completeness** — Cross-reference every MSS step, alternative flow, business rule,58 precondition, and postcondition from the spec against the handlers, services, and views59- **Acceptance Criteria** — Read the acceptance criteria from the spec file (`docs/use_cases/UC-XXX.md`) for $ARGUMENTS; verify each criterion is satisfiable60- **Code Quality** — Run `go tool templ generate && sqlc generate && go build ./... && go vet ./...`;61 verify form `Validate()` runs on every mutation at the system boundary; verify error states62 surface meaningful feedback63- **Test Coverage** — Verify unit tests exist next to the code, as `/implement` prescribes:64 `form_test.go` (validation rules), `service_test.go` (business rules, returned errors),65 `handler_test.go` (status codes, `422`, fragment vs full page, redirects), `views_test.go`66 (empty-state and error text). Every MSS step, alternative flow, and business rule is asserted67- **Privacy** — Search for hardcoded secrets; verify `.gitignore` covers `.env` and `.env.*`68- **i18n** — Only check if i18n is detected (see i18n Detection above); otherwise mark N/A69- **Configuration Management** — Verify environment profiles (`.env`, `.env.dev`) and70 `internal/config/config.go` exist7172---7374### Lens 1: i18n Completeness (file analysis — no browser) — skip if no i18n7576Identify every user-facing string introduced or modified for $ARGUMENTS:771. Verify each goes through `i18n.T(ctx, ...)` or `i18n.TN(ctx, ...)`, not a hardcoded literal.78 Search the feature's `.templ` and `.go` files for:79 - Text nodes in `.templ` files not wrapped in `{ i18n.T(ctx, "...") }`80 - Attribute literals (`placeholder`, `title`, `aria-label`, `alt`, `value` on buttons) that81 do not go through `i18n.T`82 - Literal messages returned by `Validate()` — with i18n configured, `Validate()` returns83 field → message ID and the view translates it84 - `http.Error` bodies and error fragments rendered to the user852. Verify every message ID used exists in **all** catalogs under `internal/i18n/locales/`863. Flag IDs missing from non-default locales874. Flag values with a `[TRANSLATE]` prefix (placeholder not yet translated)8889---9091### Lens 2: i18n Correctness (file analysis — no browser) — skip if no i18n9293For every message ID used by $ARGUMENTS, compare values across all catalogs:941. Verify template placeholders (`{{.Min}}`, `{{.Name}}`) match across locales — same names,95 same count — and match the data passed at the `T` / `TN` call site962. Verify plural messages (used with `TN`) define every CLDR form the locale needs — `one` and97 `other` everywhere, plus `few` / `many` where the locale requires them (e.g. `ro`, `pl`, `ru`)983. Flag translations that appear machine-translated or identical to the default locale994. Flag translations where meaning clearly diverges from the default100101---102103### Lens 3: Error Message i18n (file analysis — no browser) — skip if no i18n104105Search all implementation files for $ARGUMENTS and identify every error-handling path:106- Handler error mapping — not found → `404`, forbidden → `403`, anything else → `500`107- `422` form re-renders with field errors and the non-field error summary108- htmx error fragments109- The `Recover` middleware's `500` response110- The `401` + `HX-Redirect` and `403` responses from `internal/auth`111112For each, verify the message shown to the user uses a message ID, not a hardcoded string.113Hardcoded strings like `"Something went wrong"`, `"Invalid input"`, or `"Please try again"` are114findings. Log messages written with `slog` are not user-facing — do not flag them.115116---117118### Lens 4: Accessibility (Playwright MCP — browser required)119120Start the app if not running: `set -a; . ./.env; set +a; go run ./cmd/dev`. For each screen in121the frontend design:1221. Navigate using `browser_navigate`1232. Run axe-core. The app's CSP (`script-src 'self'`, set by `/setup-web-middleware`) blocks124 importing axe-core from a CDN inside `browser_evaluate`. Inject it from the Node side with125 `browser_run_code_unsafe` instead:126 ```javascript127 async (page) => {128 const src = await (await fetch('https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.9.1/axe.min.js')).text();129 await page.evaluate(src);130 return await page.evaluate(() => axe.run());131 }132 ```1333. Collect all violations with impact level (critical, serious, moderate, minor)1344. Additionally check:135 - Every `<img>` has a non-empty `alt` (or `alt=""` with `role="presentation"` for decorative)136 - Every form input has `<label for>` or `aria-label`137 - Focus order is logical (tab through with `browser_press_key`)138 - Focus lands somewhere sensible after an htmx swap (e.g. the first field error)139 - Color contrast meets WCAG AA (axe-core covers this, but flag if axe is unavailable)140141---142143### Lens 5: Screen Fidelity vs Design (Playwright MCP — browser required)144145For each screen in the frontend design:1461. Open the design HTML in the browser (`browser_navigate` to the file path)1472. Take a snapshot (`browser_snapshot`)1483. Navigate to the implemented screen in the running app1494. Take a snapshot1505. Compare and flag:151 - Missing components (in design, absent in implementation)152 - Layout deviations (arrangement, alignment, spacing)153 - Typography mismatches (headings, font sizes, font weights)154 - Color mismatches (background, text, border)155 - Missing states (design specifies an empty state that implementation doesn't handle)156157---158159### Lens 6: Loading, Error, and Empty States (file analysis + Playwright MCP)160161For each screen that performs async operations (htmx requests, form submissions):1621. Verify every triggering element has `hx-indicator`, and `internal/web/static/app.css` defines163 the `.htmx-indicator` rules (htmx's inline indicator styles are disabled by the CSP setup)1642. Verify the layout's `htmx-config` `responseHandling` swaps `422`, so re-rendered forms with165 validation errors actually appear1663. Submit invalid input in the browser: field errors appear next to their inputs, and non-field167 errors appear in a summary1684. Verify data screens render an explicit empty-state component (no rows → meaningful message,169 not a blank page)1705. Navigate to a missing resource (e.g. `/items/does-not-exist`): the app responds `404` with a171 page, not a `500` or a blank body1726. Verify a `5xx` on an htmx request shows the user something (an error fragment, a swapped173 error target, or an `htmx:responseError` handler in a static `.js` file) rather than silently174 doing nothing175176---177178### Lens 7: E2E Traceability (file analysis — no browser)179180Skip if `e2e/trace_test.go` does not exist (project has not adopted the traceability helper181yet).182183Otherwise, for every file `e2e/*_test.go` except `trace_test.go` and `setup_test.go`:1841851. **Build the ignore set.** If `e2e/.tracedignore` exists, parse it as a gitignore-style list186 (one path per line, `#` comments, blank lines skipped) and exclude matching files from the187 rest of the lens.1881892. **Check top-level test names.** Every top-level `func TestXxx(t *testing.T)` (excluding190 `TestMain`) is named `TestUC<NNN>` or `TestBUG<NNN>`. Any other name is a violation.1911923. **Check `TestUC<NNN>` subtests.** Every `t.Run("<scenario>/<journey>", ...)` inside193 `TestUC<NNN>` starts with `useCase(t, "UC-NNN", "<scenario>", refs...)`, where:194 - the `UC-NNN` argument matches the enclosing test name (`TestUC007` → `"UC-007"`)195 - the scenario argument is `MSS`, `AF-N`, or `EX-N` and matches the `t.Run` name prefix196 A subtest whose first statement is not `useCase(...)`, or whose arguments disagree with the197 test or subtest name, is a violation.1981994. **Check `TestBUG<NNN>` tests.** The first statement is `bug(t, "BUG-NNN")` with the ID200 matching the test name.2012025. **Check references.** For every `useCase(t, "UC-NNN", scenario, "CR-NNN", "BUG-NNN", ...)`203 and `bug(t, "BUG-NNN")` literal: confirm the referenced doc exists under `docs/use_cases/`,204 `docs/change_requests/`, or `docs/bugs/`. The helper enforces this at runtime; the audit205 catches it before a test run.2062076. **Check the status of each referenced `BUG-NNN`.** If the bug file shows a status of208 `RESOLVED` (or equivalent), this is correct. If the status is still `OPEN`, the test is209 guarding against a regression of an unfixed bug — flag as Minor and ask whether the bug210 should be marked resolved.211212For each violation, report file path, line number, and the specific rule broken. Severity:213**Major** during rollout (advisory). Once one full cluster has been delivered cleanly under the214helper, escalate to **Critical** and have the audit fail the merge gate.215216---217218## Output Format219220Produce one section per lens. For each finding:221- **Severity:** Critical / Major / Minor222- **Location:** file path and line number (or screen name for browser lenses)223- **Finding:** what is wrong224- **Fix:** how to fix it225226**Severity guide:**227- **Critical** — DoD item FAIL, user sees broken/untranslated text, app crashes or panics, WCAG A violation228- **Major** — Missing translation in non-default locale, WCAG AA violation, missing error state,229 missing unit tests for a business rule or alternative flow230- **Minor** — Placeholder not yet translated, minor style deviation, missing decorative alt text231232## Verification233234The audit is complete when:235- Every lens has a section marked with findings, **no findings**, or **N/A** with the reason236- Every finding has severity, location, finding, and fix237- The report ends with a summary:238 - Total findings by severity239 - Verdict: **PASS** (no findings) | **PASS WITH OBSERVATIONS** (0 Critical, Minor only) | **FAIL** (any Critical or Major)