softr-vibe-coding 2.5.0 → 2.5.1

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,9 @@ 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
+ ## [2.5.1] - 2026-09-09
8
+ - The Action-permission reset has a failure mode that leaves blocks publicly writable — verify, never assume
9
+
7
10
  ## [2.5.0] - 2026-09-09
8
11
  - Cross-block and cross-page consistency: the failure mode where every block looks correct alone
9
12
 
package/README.md CHANGED
@@ -291,7 +291,7 @@ The skill enforces these automatically, but good to know (verified live against
291
291
  - Create payloads are **flat**; update payloads are `{ recordId, fields: {...} }` — asymmetric by design
292
292
  - `mutateAsync` is fully supported — it's the tool for sequential multi-row saves
293
293
  - SELECT fields write by option **label string**; linked records write as arrays of record-id strings
294
- - Every code recompile resets the block's auto-registered Actions to default permissions — tighten permissions after the last redeploy, and note there is **no cosmetic-edit exemption**: an edit that changes only a comment resets them too
294
+ - Every code recompile resets the block's auto-registered Actions to default permissions — tighten permissions after the last redeploy, and note there is **no cosmetic-edit exemption**: an edit that changes only a comment resets them too. **Always read the permissions back to confirm** — the default for a create action is `ALL_USERS` (publicly writable), and the MCP call that re-tightens it can fail with no fallback
295
295
  - **Blocks cannot import each other**, so two blocks that must look alike will drift — each one looks correct in isolation while the set does not. Repeated page chrome (back button, title, primary action) must sit at the same offset on every page, and a loading skeleton must track the REST state of whatever it stands in for
296
296
  - No `import React from 'react'` — use named imports (`import { useState } from "react"`)
297
297
  - Must use `export default function Block()`
package/SKILL.md CHANGED
@@ -548,8 +548,15 @@ Non-negotiable rules. Most are enforced by the Softr platform (compiler, validat
548
548
  across a 15-block deployment. See [datasources/writing.md](datasources/writing.md#how-actions-work-studios-actions-tab).
549
549
  **Any save counts, including one whose only change is a comment** -- there is no "cosmetic edit"
550
550
  exemption; a `search_replace` that rewrites nothing but a code comment rebuilds the Actions exactly
551
- like a full rewrite does (verified live 2026-09-09, on two blocks at once). Re-check permissions
552
- after EVERY push.
551
+ like a full rewrite does (verified live 2026-09-09, on two blocks at once).
552
+ **Re-read the permissions after EVERY push and confirm they actually changed** -- do not assume the
553
+ restore worked. Softr's default for a `genericActions` ADD_RECORD is `ALL_USERS`, i.e. writable by
554
+ logged-OUT visitors, and the MCP call that re-tightens it (`set_vibe_coding_block_action_visibility`)
555
+ can itself fail with no fallback (see the array-argument quirk in
556
+ [references/softr-mcp.md](references/softr-mcp.md#the-array-argument-serialization-quirk-and-why-it-is-a-security-issue)).
557
+ A push that returns `errors: null` can still have left public write access on the block.
558
+ **If any action is still `ALL_USERS`, do not publish** -- surface the list and have a human set it
559
+ in Studio.
553
560
  22. **Blocks cannot import each other -- cross-block consistency is discipline, not architecture [house]**
554
561
  -- Every block compiles standalone. There is no shared module, no design-system import, nothing that
555
562
  makes two blocks stay alike. Two blocks that must look the same WILL drift, and the drift usually
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "softr-vibe-coding",
3
- "version": "2.5.0",
3
+ "version": "2.5.1",
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"
@@ -39,6 +39,7 @@ Run through this catalog before delivering any block. Every row is a violation o
39
39
  | Passing a data hook's options through a variable or wrapper function: `useRecords(buildOpts())` | **Fails to compile** — the options object must be an inline literal at the call site (verified live 2026-08-25; hit in production, fixed by making the wrapper take the hook's *result* instead). Share `q.select` mappings between hooks, never whole options objects |
40
40
  | Tightening Actions-tab permissions before the block's final redeploy | Every code recompile **resets the auto-registered Actions to default permissions** (verified live 2026-08-25). Tighten permissions after the LAST redeploy, and re-check after any future one |
41
41
  | Assuming a comment-only edit is "safe" and leaves Action permissions alone | There is no cosmetic-edit exemption. Any save recompiles, and every recompile rebuilds the Actions at default visibility — a `search_replace` changing nothing but a code comment resets them exactly like a rewrite (verified live 2026-09-09, on two blocks at once). Re-check after EVERY push, including cosmetic ones |
42
+ | Treating the permission-restore call as done because you issued it | Read the permissions back with `get_vibe_coding_block_settings` and confirm each one changed. `set_vibe_coding_block_action_visibility` can fail outright on the array-argument serialization quirk, and it has **no fallback** — the default for a `genericActions` ADD_RECORD is `ALL_USERS`, so a routine push silently leaves the block publicly writable while returning `errors: null`. Verified live 2026-09-09: four ADD_RECORD actions left open across two blocks. **Do not publish** until they read `LOGGED_IN_USERS`; escalate to a human with Studio access |
42
43
  | 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 |
43
44
  | One alias in a write-side `q.select` referencing a renamed / non-existent Airtable column | Softr's Action parser silently rejects the **entire** create/update Action — not just the bad alias. Symptoms: Studio's Actions tab shows "No actions used in this block yet", `createRecord.enabled` / `updateRecord.enabled` stays `false`, `.mutate()` calls dispatch but resolve immediately to "not yet ready". Every OTHER field in the same `q.select()` is also lost, even the ones that map cleanly. Diagnostic: bisect the `q.select` — strip down to a known-good minimal set, confirm the Action appears in Studio, then add fields back in halves until it drops out. The culprit is in the last half added. Once narrowed to a single field, grep its name against the freshest Airtable schema export to catch the rename / trailing-space / case-mismatch. Verified 2026-05-21: a `"Photos"` column on Wigs was renamed to `"Before Photos"`, the helper that wrote `photos: "Photos"` had its entire Action disabled even though 11 other fields in the same `q.select` were fine. See [datasources/airtable.md](../datasources/airtable.md#maintainability-gotcha) |
44
45
 
@@ -101,14 +101,52 @@ components, adding a hook — where being sure of "the exact current text" of a
101
101
  is harder than being sure of the whole file. Also use it when the local file is the source of truth and
102
102
  has drifted from the deployed block in ways you have not enumerated.
103
103
 
104
- **Caveat on the `operations` argument.** It is an array of `{search, replace}` objects. Some MCP clients
105
- serialize it as a JSON *string* instead, and the API rejects that with a Jackson error — `Cannot
106
- deserialize value of type java.util.ArrayList<java.util.Map<String,String>> from String value` (hit
107
- 2026-09-09, mid-session, on a tool that had accepted the same shape minutes earlier). It is a client
108
- serialization quirk, not a bad request: if it will not take the array, fall back to the full replace
109
- rather than mangling the edit to fit.
110
-
111
- Both paths recompile, so both reset Action permissions either way (Hard Constraint 21).
104
+ ### The array-argument serialization quirk, and why it is a security issue
105
+
106
+ **Several tools on this server take an array argument, and some MCP clients serialize it as a JSON
107
+ *string* instead.** The API then rejects it with a Jackson error before it reaches any business logic:
108
+
109
+ ```
110
+ Cannot deserialize value of type `java.util.ArrayList<java.util.Map<String,Object>>`
111
+ from String value (token `JsonToken.VALUE_STRING`)
112
+ ```
113
+
114
+ Root cause: the server advertises an **empty schema** for its tools (`{"type":"object"}`, no property
115
+ definitions), so a client has no type information to serialize against. Confirmed 2026-09-09 on:
116
+
117
+ | Tool | Array argument | Fallback if it fails |
118
+ |---|---|---|
119
+ | `update_vibe_coding_block_code_search_replace` | `operations` | Use `update_vibe_coding_block_code` (full replace) |
120
+ | `set_vibe_coding_block_action_visibility` | `updates` | **NONE — a human must fix it in Studio** |
121
+
122
+ It is intermittent, and that is the trap: on 2026-09-09 both tools accepted the array early in a
123
+ session and rejected it an hour later, same shapes, same session. Do not conclude from one success
124
+ that the path is reliable for the rest of your work.
125
+
126
+ **Why the second row is a security problem, not an inconvenience.** Every code push resets the block's
127
+ auto-registered Actions to Softr's defaults, and the default for a `genericActions` **ADD_RECORD is
128
+ `ALL_USERS`** — writable by logged-OUT visitors. The documented remedy is to re-tighten with
129
+ `set_vibe_coding_block_action_visibility`. When that call is the one that fails, a routine cosmetic push
130
+ silently leaves public write access on the block, and nothing in the push result says so: the push
131
+ itself returns `errors: null, warnings: null`. Verified live 2026-09-09 — one push left four ADD_RECORD
132
+ actions open across two blocks.
133
+
134
+ **So treat permission restoration as a step that must be VERIFIED, never assumed:**
135
+
136
+ 1. Push the code.
137
+ 2. Call `set_vibe_coding_block_action_visibility` for every action that needs tightening.
138
+ 3. **Read the permissions back with `get_vibe_coding_block_settings` and confirm each one actually
139
+ changed.** A successful-looking sequence is not evidence; the failure is an argument rejection, so
140
+ the call errors rather than lying, but an agent that batches calls can easily miss which one failed.
141
+ 4. If any action is still `ALL_USERS`, **do not publish.** Report the exact list — page, block, action
142
+ type, data source — and have a human set them on the block's Actions tab in Studio.
143
+
144
+ Do not improvise around a rejection. `update_vibe_coding_block_settings` is not a substitute: its schema
145
+ is equally empty, it writes far more than one permission, and guessing its payload risks clobbering the
146
+ block's data source connections. Restoring an older block version is not a substitute either — it
147
+ reverts the code along with the permissions, undoing the change you just pushed.
148
+
149
+ Both edit paths recompile, so both reset Action permissions either way (Hard Constraint 21).
112
150
 
113
151
  ## Adopting Studio-AI-generated code
114
152