From 1d5a95196f5d8ffe70f131dcf7eae9522a339306 Mon Sep 17 00:00:00 2001 From: Bryce Date: Wed, 3 Jun 2026 06:34:43 -0700 Subject: [PATCH] docs(skill): add cookbook entry for de-faking a fixed-index row from explicit data Captures the reusable pattern proven on simple-mode-fields*: render a known-index row (accounts[0]) from explicit data with explicit field names (path->name2 equivalent) + explicit error lookup, instead of faking a deep cursor. Pairs with the gotcha that with-field-default mutates the cursor. Growth contract for the de-fake commit. --- .../reference/component-cookbook.md | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/.claude/skills/ssr-form-migration/reference/component-cookbook.md b/.claude/skills/ssr-form-migration/reference/component-cookbook.md index 36549f26..f9332f10 100644 --- a/.claude/skills/ssr-form-migration/reference/component-cookbook.md +++ b/.claude/skills/ssr-form-migration/reference/component-cookbook.md @@ -90,6 +90,35 @@ replaces the amount input (caret survives). :placeholder "Optional note"}) ; no hx-* — rides along to save ``` +## fixed-index row from explicit data — de-faking a deep cursor + +When a row always lives at a known index (e.g. simple mode renders exactly `accounts[0]`), +render it from **explicit data with explicit field names** instead of faking a cursor +rooted there. Build the name the same way the cursor would (`path->name2`) and read errors +from the same path — no `with-cursor`/`MapCursor` rebind, no `with-field-default` (which +*mutates* the cursor and breaks swap behavior, see `gotchas.md`). + +```clojure +(defn- account-field-name [index field] ; == path->name2 for this path + (str "step-params[transaction/accounts][" index "][" + (if (keyword? field) + (str (when (namespace field) (str (namespace field) "/")) (name field)) + field) "]")) + +(defn- account-field-errors [index field] + (when (bound? #'fc/*form-errors*) + (get-in fc/*form-errors* [:step-params :transaction/accounts index field]))) + +;; render the row directly -- no fc/with-field / fc/with-cursor wrappers +[:span + (com/hidden {:name (account-field-name 0 :db/id) :value row-id}) + (com/validated-field {:errors (account-field-errors 0 :transaction-account/account)} + (account-typeahead* {:name (account-field-name 0 :transaction-account/account) ...})) + ...] +``` +Verify byte-parity against the cursor version (the swap spec's simple-mode tests catch +divergence). Scorecard heuristic 1: faked roots → 0. + ## mode toggle ($/% radio, simple/advanced link) — Rule 3, whole-form swap ```clojure