Files
integreat/.claude/skills/ssr-form-migration/reference/scorecard.md
Bryce a01dfc197e refactor(ssr): full Selmer migration of Transaction Edit; remove the wizard
Migrate every part of the Transaction Edit modal's HTML to Selmer templates
(zero Hiccup in the render path) and delete the mm multi-modal "wizard"
abstraction entirely -- there was only ever one step.

- New auto-ap.ssr.components.selmer (sc) + ~22 shared component partials under
  resources/templates/components/ (typeahead, button-group, radio-card,
  data-grid, validated-field, modal, buttons, inputs, SVGs). Each wrapper renders
  its own partial; dynamic HTMX/Alpine attrs bridge via attrs->str -> {{attrs|safe}}.
- 15 modal templates under resources/templates/transaction-edit/.
- Delete EditWizard/LinksStep records + all mm/* usage. Plain handlers: flat
  wrap-decode-edit (fields renamed off step-params[...], stray keys stripped),
  flat wrap-derive-state, *errors*-based field errors, generic wrap-form-4xx-2.
- Drop the edit-wizard-navigate route (routes ~12 -> 5).
- Fix: stray `method` (tab button-group hidden) leaked into the upsert -> 500;
  strip decoded map to schema keys.
- e2e selectors updated (#wizard-form->#edit-form, #wizardmodal->#editmodal,
  step-params[...] field names). Parity: swap 6/6, edit 8/8, suite 38/1
  (1 pre-existing unrelated nav test).
- ssr-form-migration skill updated with the learnings (composition mechanics,
  sc/* library, drop-the-wizard recipe, scorecard row, 3 new gotchas).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 07:47:47 -07:00

5.7 KiB

Quality scorecard (the ratchet)

Cheap to measure (grep -c, wc -l, clj-kondo), recorded before/after each migration in the commit message and in the results table below. No metric may regress for the touched modal without a written exception in gotchas.md. These are directional evidence, not targets to game — always paired with the e2e parity gate.

Heuristics

# Heuristic Measure Target
1 Faked cursor positions (not cursors themselves) grep -cE 'with-cursor|MapCursor\.' re-roots + grep -c 'defn.*-no-cursor' → 0 (top-rooted cursors are fine)
2 Implicit state merges (snapshot/cursor) count merge sites → 0 (forms); explicit put-step only (wizards)
3 Branching complexity clj-kondo, or count cond/condp/case/nested if + max depth net ↓
4 Lines of code wc -l on the modal's file(s) net ↓
5 Reuse / cross-form similarity cookbook components reused; duplicated-block count reuse ↑, dup ↓
6 Route count count routes for the modal → 2 (+1 for add-row)
7 OOB swaps grep -c hx-swap-oob → 0 unless a justified disjoint-region case is documented
8 Attribute consistency mixed :x-/"x-" encodings in migrated template → 0

How to measure (copy/paste)

F=src/clj/auto_ap/ssr/<modal>.clj
echo "LOC                $(wc -l < $F)"
echo "no-cursor twins    $(grep -c 'defn.*-no-cursor' $F)"
echo "faked-cursor roots $(grep -cE 'with-cursor|MapCursor\.' $F)"
echo "snapshot merges    $(grep -c ':multi-form-state :snapshot' $F)"
echo "branch forms       $(grep -cE '\(cond |\(condp |\(case |\(when-not ' $F)"
echo "hx-swap-oob        $(grep -c 'hx-swap-oob' $F)"
echo "mixed string hx-   $(grep -cE '\"hx-[a-z]' $F)"
# route count: count this modal's entries in src/cljc/auto_ap/routes/*.cljc

Results

Each migration appends one row (after-numbers), referencing the before in the diff.

Phase Modal LOC Routes no-cursor twins faked roots snapshot merges OOB mixed hx- cookbook reused / added
1 (baseline) Transaction Edit transaction/edit.clj 1608 ~12 1 2 ~75 0 8 — / seeded 7 entries
2 Transaction Edit transaction/edit.clj 1593 ~5 0 0 0 round-trip 0 8 (shared) location-select / 1 Selmer
2-final Transaction Edit (full Selmer + wizard removed) 1548 5 0 0 0 0 0 full sc/* lib / ~30 partials

New heuristics introduced at 2-final (full Selmer)

# Heuristic Measure Target
9 Hiccup HTML tags in the render path grep -cE '\[:(div|span|p|a|button|input|h[1-6]|ul|li|label|select|option|t(able|head|body|r|d|h)|form|svg|template)' over the modal's render fns → 0 (success-modal confirmation dialogs may keep the shared Hiccup component)
10 mm wizard coupling grep -c 'mm/' the modal file + grep -c 'defrecord.*Wizard|ModalWizardStep' → 0 for a single-step modal

Phase 2 progress. Achieved with parity held (swap spec 6/6, transaction-edit spec 8/8, full suite 38 pass / 1 unrelated fail / 0 skip, up from 30/2/7):

  • deleted the dead *-no-cursor* twin (no-cursor 1→0);
  • de-faked the simple-mode cursor (faked roots 2→0) via explicit data + explicit field names (account-field-name) + explicit error lookup — the render-fn rewrite the with-field-default shortcut couldn't do;
  • collapsed the 5 manual-coding operation routes into one edit-form-changed dispatcher (routes ~12→~5; the operations are now pure apply-* fns);
  • fixed a real production bug (:mode → 500 on every advanced manual save);
  • greened transaction-edit.spec.ts (8/8) and matured the skill.

Phase 2 complete. The wizard→plain-form rewrite removed the snapshot round-trip (heuristic 2 → 0) and the first interactive component (location-select) is migrated to a Selmer template (selmer-conventions.md validated). Remaining for later phases: drop the now-thin mm/ModalWizardStep protocol wrappers, and the cross-cutting Phase 11 Selmer sweep of the shared com/typeahead/com/select/com/button-group-button (those shared call sites hold the last 8 mixed @/:-attr offenders; they clear when the shared components move to Selmer — not a single-modal task, per Open decision 2).

Phase 2-final — full Selmer + wizard removed. Every component the modal renders through was ported to a Selmer partial under resources/templates/components/ with a thin Clojure wrapper in auto-ap.ssr.components.selmer (sc/*); the modal's own structure lives under resources/templates/transaction-edit/. The mm wizard abstraction (EditWizard/LinksStep records, MultiStepFormState, step-params[...] field names, wrap-wizard/wrap-decode-multi-form-state middleware) was deleted — there was only ever one step, so it was pure overhead. Result: heuristic 8 (mixed hx-) and 9 (Hiccup in render) and 10 (mm coupling) all → 0; the edit-wizard-navigate route is gone (routes 5). Parity held: swap spec 6/6, transaction-edit spec 8/8, full suite 38 pass / 1 pre-existing unrelated fail (serial, fresh seed). The only Hiccup left in the file is the post-save com/success-modal confirmation dialogs (terminal, shared component — out of the form's render path). See form-vs-wizard.md (drop-the- wizard test), selmer-conventions.md (composition mechanics), and gotchas.md (stray-field decode leak; jetty reload staleness).