softr-vibe-coding 1.5.0 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,12 @@ All notable changes to this skill are documented here. Versions follow [Semantic
4
4
 
5
5
  Entries from 1.3.1 onward are generated automatically from git commit subjects between version bumps (see `.github/workflows/publish.yml`). Entries before 1.3.1 were backfilled by hand from the existing commit history.
6
6
 
7
+ ## [1.5.2] - 2026-05-27
8
+ - Document BLANK-guard convention for Airtable formulas — Airtable surfaces #ERROR! / #NaN! when arithmetic, date, or string operations touch a blank field and propagates the error through every downstream formula; add a quick-rule bullet calling out the failure modes (multiply/divide-by-blank, DATEADD on blank date), show the IF({field}, <expr>, BLANK()) and AND()-guarded shapes, note why explicit guards beat catch-all IFERROR (don't mask typos), and re-render the common-patterns block with guards applied to the date examples; bump to 1.5.2
9
+
10
+ ## [1.5.1] - 2026-05-21
11
+ - Expand the "formulas in create/update" anti-pattern to cover every read-only field type (rollup / aiText / lookup / createdTime / lastModifiedTime / autoNumber, not just formulas), document the silent all-or-nothing parser failure (Actions tab empty, enabled stays false, every other writable field in the same q.select is lost), cite the verified 2026-05-22 wig-details-page.jsx case (Wig Tag ID formula + Total client/worker rollups + Instrucciones aiText all shared with useRecordUpdate), and point at the bisection diagnostic; bump to 1.5.1
12
+
7
13
  ## [1.5.0] - 2026-05-21
8
14
  - Document write-side Action-disabled failure when a q.select alias references a renamed/missing Airtable column — extend datasources/airtable.md "Maintainability gotcha" with read-vs-write asymmetry (reads silently degrade, writes silently disable the entire Action), add the bisection diagnostic procedure for the "No actions used in this block yet" symptom, and add an anti-patterns Mutations-table row citing the verified 2026-05-21 Photos→Before Photos case; bump to 1.5.0
9
15
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "softr-vibe-coding",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "Claude Code skill for generating production-ready Softr Vibe Coding blocks (JSX). Installs into ~/.claude/skills/ and auto-updates on each Claude Code session.",
5
5
  "bin": {
6
6
  "softr-vibe-coding": "./bin/cli.js"
@@ -321,22 +321,31 @@ For Automation Scripts, your ONLY user-visible surfaces are `console.log()` (run
321
321
  For formula fields inside Airtable itself (NOT scripts). Quick rules:
322
322
 
323
323
  - **NEVER add comments inside Airtable formulas.** Airtable's formula engine doesn't have a comment syntax — anything that looks like a comment will fail to compile.
324
+ - **Guard against blank inputs by default.** Airtable surfaces `#ERROR!` (or `#NaN!` for divide-by-blank) when arithmetic, date, or many string operations touch a blank field — `{A} * {B}` errors when either is blank, `{A} / {B}` errors when `{B}` is blank, `DATEADD({Start At}, 20, 'minutes')` errors when `{Start At}` is blank, and one upstream `#ERROR!` propagates through every downstream formula that references it. Wrap any formula whose inputs could be empty with an `IF()` guard returning `BLANK()` in the empty branch. Prefer explicit `IF()` / `AND()` guards over a catch-all `IFERROR(<expr>, BLANK())` — explicit guards keep intent readable and don't mask unrelated bugs (a typo'd field name silently returns blank instead of failing loudly). Reach for `IFERROR()` only when inputs come from genuinely uncontrolled sources.
325
+
326
+ ```
327
+ IF({Start At}, DATEADD({Start At}, 20, 'minutes'), BLANK())
328
+ IF(AND({Quantity}, {Price}), {Quantity} * {Price}, BLANK())
329
+ IF({Divisor}, {Numerator} / {Divisor}, BLANK())
330
+ ```
324
331
  - Reference fields by name with curly braces: `{Field Name}` (single field) or `{Other Table}` is not valid — formulas are scoped to the current table only.
325
332
  - Functions are case-insensitive (`IF` = `if`) but field names ARE case-sensitive.
326
333
  - Use `&` for concatenation, NOT `+` (the latter is numeric).
327
334
  - For conditional logic, prefer `IF()` nested or `SWITCH()` for many branches.
328
335
 
329
- Common patterns:
336
+ Common patterns (blank-guards applied where inputs could be empty):
330
337
 
331
338
  ```
332
339
  IF({Status} = "Paid", "✓", "")
333
340
 
334
341
  CONCATENATE({First Name}, " ", {Last Name})
335
342
 
336
- DATETIME_FORMAT({Created}, 'YYYY-MM-DD')
343
+ IF({Created}, DATETIME_FORMAT({Created}, 'YYYY-MM-DD'), BLANK())
337
344
 
338
345
  IF(AND({Amount} > 100, {Paid} = TRUE()), "VIP", "Standard")
339
346
 
347
+ IF({Start At}, DATEADD({Start At}, 20, 'minutes'), BLANK())
348
+
340
349
  SWITCH({Tier},
341
350
  "gold", "Premium",
342
351
  "silver", "Standard",
@@ -27,7 +27,7 @@ Run through this catalog before delivering any block. Every row is a violation o
27
27
  | `deleteRecord.mutate({ id: r.id })` | `deleteRecord.mutate(r.id)` -- just the string |
28
28
  | `var { mutateAsync } = useRecordUpdate({...})` | `var updateRecord = useRecordUpdate({...})` -- keep full object for `.enabled`, `.status`, `.reset()` |
29
29
  | Not calling `refetch()` after mutations | Always `refetch()` in `onSuccess` |
30
- | Including formulas in create/update | Only writable fields |
30
+ | Including read-only fields (formula / rollup / aiText / lookup / createdTime / lastModifiedTime / autoNumber) in the `fields` q.select passed to `useRecordCreate` or `useRecordUpdate` | Softr's Action parser silently rejects the **entire** create/update Action not just the bad alias. Same all-or-nothing failure mode as a renamed/missing column: Studio's Actions tab shows "No actions used in this block yet", `createRecord.enabled` / `updateRecord.enabled` stays `false`, `.mutate()` calls dispatch but resolve to "not yet ready", every OTHER writable field in the same q.select is also lost. Reads handle these field types fine — only the write q.select chokes. Fix: split into separate q.selects per the Three Mappings Pattern (`useRecord` / `useRecords` gets the full select with read-only fields; `useRecordCreate` / `useRecordUpdate` gets a writable-only subset). Diagnostic when the symptom shows up: same bisection procedure as the renamed-column case — strip the write q.select to a known-writable minimum, then add fields back in halves until the Action drops out. Verified 2026-05-22: `blocks/wig-details/wig-details-page.jsx` shared one `wigSelect` between `useRecord` and `useRecordUpdate`; the select included `Wig Tag ID` (formula), `Total client price` (rollup), `Total worker pay` (rollup), `Instrucciones` (aiText) — Update Action stayed disabled until those four read-only fields were lifted out into a separate write-only select. See [datasources/airtable.md](../datasources/airtable.md#maintainability-gotcha) and [datasources/writing.md](../datasources/writing.md). |
31
31
  | Linked record as plain string | Must be `[{ id: "..." }]` array |
32
32
  | Writing dropdown values as `{ id, label }` objects (the read shape) | Write the option UUID as a plain string -- e.g. `status: "822b8d69-..."`, not `status: { id: "...", label: "..." }` |
33
33
  | Treating Studio's Actions tab as a separately-managed configuration to keep in sync with code | Actions auto-derive from your `useRecordCreate`/`useRecordUpdate`/`useRecordDelete` + `q.select` on every save. The Actions tab is a read-only inspector; there is no manual delete control. To change an Action, change the code |