refactor(ssr): full Selmer migration of Transaction Edit; remove the wizard
Squashed Phase-2 SSR work: migrate the Transaction Edit modal's render path entirely to Selmer templates (zero Hiccup in the render path), rip out the multi-step wizard abstraction (EditWizard/LinksStep records, MultiStepFormState, step-params[...] field names, mm/* middleware) in favor of a plain form with flat derived state, and promote shared UI components to reusable Selmer partials under resources/templates/components/. Adds the Selmer interop bridge, the auto-ap.ssr.components.selmer (sc) wrapper library, and the ssr-form-migration skill capturing the learnings. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
84
.claude/skills/ssr-form-migration/reference/scorecard.md
Normal file
84
.claude/skills/ssr-form-migration/reference/scorecard.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# 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)
|
||||
|
||||
```bash
|
||||
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).
|
||||
Reference in New Issue
Block a user