# Render functions: explicit data, or a top-rooted cursor

**One function, data in, markup out.** The data can arrive as a plain map *or* via a
cursor — as long as the cursor was rooted at the top of the form and walked down to here,
never faked to start at this depth. The rule is about *where the cursor starts*, not
whether you use one.

## GOOD — explicit data, pure, testable without setup

```clojure
(defn account-row [{:keys [account index client-id amount-mode]}]
  (com/data-grid-row
    (com/hidden {:name (str "accounts[" index "][db/id]")
                 :value (or (:db/id account) "")})
    (com/data-grid-cell
      (account-typeahead* {:value (:transaction-account/account account)
                           :name  (str "accounts[" index "][account]")
                           :client-id client-id}))
    ...))
```

## ALSO FINE — a cursor that started at the form root and was advanced naturally

```clojure
;; The top-level render walks the cursor; the row fn receives the dereferenced row
;; (or the advanced cursor). No rebinding of *current*/*prefix* to fake depth.
(defn account-rows [accounts-cursor]
  (for [row-cursor (fc/each accounts-cursor)]   ; advanced from the root, not faked
    (account-row {:account @row-cursor :index (fc/index row-cursor) ...})))
```

`transaction/edit.clj`'s `transaction-account-row*` is the cursor form done right: the
caller (`account-grid-body*`) holds a top-rooted cursor via `fc/cursor-map` and hands each
row cursor to one render fn.

---

## The SMELL this migration removes

### 1. Faking the cursor's starting position

A "form cursor" is fine. The pain is **rebinding the dynamic root deeper in the tree** so
a deeply nested render fn can run against a fragment. Real example from
`transaction/edit.clj`'s `simple-mode-fields*` (the thing to delete):

```clojure
;; SMELL: re-roots the cursor to a synthetic MapCursor pointed at accounts[0] so a
;; fragment can render "deep". Fragile, and the source of the *-no-cursor* twin below.
(fc/with-field :transaction/accounts
  (fc/with-cursor (let [cur fc/*current*]
                    (if (sequential? @cur)
                      (nth cur 0 nil)
                      (auto_ap.cursor.MapCursor. {} (cursor/state cur)
                                                 (conj (cursor/path cur) 0))))
    ...))
```

Target: the cursor begins at the top level of what the form consumes and walks down
naturally. Because the **whole form is re-rendered each time** (swap doctrine), there is
no longer any reason to fake a deep starting position.

### 2. The `*-no-cursor*` twin

Faking the deep cursor forces a *second copy of the same markup* — one that reads the
faked cursor and one that takes plain params for the cases where the fake can't be set up.
`transaction/edit.clj` has exactly this pair:

```clojure
(defn transaction-account-row*           [{:keys [value index client-id ...]}] ...) ; cursor form
(defn transaction-account-row-no-cursor* [{:keys [account index client-id ...]}] ...) ; duplicate markup
```

**Fix:** keep one render fn. If a caller already holds a top-rooted cursor, advance it and
hand the row data (or the advanced cursor) to that one fn. Delete the `*-no-cursor*` copy.
Heuristic 1 targets `grep -c 'defn.*-no-cursor'` → 0 and faked-cursor re-roots → 0.

## Scorecard hooks (heuristics 1, 2)

```bash
grep -c 'defn.*-no-cursor' $F           # → 0
grep -cE 'with-cursor|MapCursor\.' $F   # faked re-roots → 0 (top-rooted cursors are fine)
```

Top-rooted cursors do **not** count against heuristic 1 — only *re-roots that fake depth*
and the `*-no-cursor*` twins do.
